I have three rooms General, TypeScript and Nest. I'am joining General room in the first and second browser tab. When I decide to leave General from the second tab and join TypeScript room I still get the messages from General even though I should have left it. The bug occures when you switch to room that has been in use by other client and leave it. I'am using NodeJs, Socket.io, NestJS and React.
Server
#WebSocketGateway()
export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
#WebSocketServer() server: Server;
private logger: Logger = new Logger('ChatGateway');
#SubscribeMessage('msgToServer')
handleMessage(client: Socket, message: { sender: string, room: string, message: string }): void {
this.server.to(message.room).emit('msgToClient', message);
this.logger.log(`msgToClient: ${message}`);
}
#SubscribeMessage('joinRoom')
handleRoomJoin(client: Socket, room: string ) {
client.join(room);
client.emit('joinedRoom', room);
this.logger.log(`Client joined ${room}`);
}
afterInit(server: Server) {
this.logger.log('Init');
}
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
}
handleConnection(client: Socket, ...args: any[]) {
this.logger.log(`Client connected: ${client.id}`);
}
#SubscribeMessage('leaveRoom')
handleRoomLeave(client: Socket, room: string ) {
console.log(client.id);
/* console.log(client.rooms);
console.log(client.adapter.rooms); */
client.leave(room);
/* console.log(room); */
/* client.rooms = {};
delete client.adapter.rooms[room]; */
client.emit('leftRoom', room);
this.logger.log(`Client left ${room}`);
/* console.log(client.adapter.rooms);
console.log(client.rooms); */
// console.log(client.adapter.rooms)
}
}
Client
const [state, setState] = useState({ sender: "", room: "", message: "" });
const [chat, setChat] = useState([
{ sender: "Peter", room: "General", message: "test" },
]);
const socket = io(BASE_URL);
const [room, setRoom] = useState({
General: false,
TypeScript: false,
NestJS: false,
});
console.log(room);
useEffect(() => {
socket.on("msgToClient", (msg) => {
setChat([...chat, { ...msg }]);
});
});
const onTextChange = (e) => {
setState({ ...state, [e.target.name]: e.target.value });
};
const onMessageSubmit = (e) => {
e.preventDefault();
const { sender, room, message } = state;
socket.emit("msgToServer", { sender, room, message });
setState({ message: "", sender: "", room: "" });
};
const toggleRoomMembership = (chatroom) => {
const isMemberOfActiveRoom = (chatroom) => {
return room[chatroom];
};
if (isMemberOfActiveRoom(chatroom)) {
setRoom({ ...room, [chatroom]: false });
socket.emit("leaveRoom", chatroom);
} else {
setRoom({ ...room, [chatroom]: true });
socket.emit("joinRoom", chatroom);
}
};
const renderChat = () => {
return chat.map(({ sender, message }, index) => (
<div key={index}>
<h3>
{sender}: <span>{message}</span>
</h3>
</div>
));
};
Turns out the solution is simple. Put the socket into the useEffect and create state for it.
const [socket, setSocket] = useState({});
useEffect(() => {
const socket = io(BASE_URL);
setSocket(socket);
socket.on("msgToClient", (msg) => {
setChat([...chat, { ...msg }]);
});
}, []);
Related
import React, { useEffect, useContext, useRef } from 'react';
import { SocketContext } from "../../contexts/socketContext";
const Call = ({ myId, otherId, init }) => {
const socket = useContext(SocketContext);
const localStream = useRef();
const remoteStream = useRef();
const _pc = useRef(new RTCPeerConnection());
useEffect(() => {
navigator.mediaDevices.getUserMedia({ audio: true, video: true }).then((stream) => {
localStream.current.srcObject = stream;
stream.getTracks().forEach((track) => {
_pc.current.addTrack(track, stream);
});
}).catch((err) => {
console.log(err);
});
if (init) {
createAndSendOffer()
}
_pc.current.onicecandidate = (e) => {
if (e.candidate) {
socket.emit("candidate", { from: myId, to: otherId, candidate: e.candidate });
}
}
_pc.current.ontrack = (e) => {
console.log("Hello");
if (e.streams) {
console.log(e.streams);
}
}
});
useEffect(() => {
socket.on("offer", (data) => {
createAndSendAnswer(data);
});
socket.on("answer", (data) => {
setAnswer(data.answer);
});
socket.on("candidate", (data) => {
_pc.current.addIceCandidate(new RTCIceCandidate(data.candidate));
});
});
async function setAnswer(answer) {
_pc.current.setRemoteDescription(new RTCSessionDescription(answer));
}
async function createAndSendOffer() {
const offer = await _pc.current.createOffer({ offerToReceiveAudio: true, offerToReceiveVideo: true });
socket.emit("offer", { from: myId, to: otherId, offer });
_pc.current.setLocalDescription(new RTCSessionDescription(offer));
}
async function createAndSendAnswer({ from, to, offer }) {
_pc.current.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await _pc.current.createAnswer();
_pc.current.setLocalDescription(new RTCSessionDescription(answer));
socket.emit("answer", { from: myId, to: otherId, answer });
}
return (
<div>
<video autoPlay muted ref={localStream} />
<video autoPlay muted ref={remoteStream} />
</div>
)
}
export default Call
``
I am trying to make a video calling app but remote peer video is not getting.
I am Creating a chat application everything is almost done I can send messages and the server is receiving the data but can't receive the data from the socket.io server
basically, I created a separate server that connects the clients it didn't connect to the database. I tried everything but couldn't help
SocketServer.js
const io = require('socket.io')(9739, {
cors: {
origin: "*",
}
})
let activeUsers = [];
io.on('connection', (socket) => {
console.log('New user connected');
socket.on('login', (data) => {
console.log(data);
if (!activeUsers.some((user) => user.id === data)) {
activeUsers.push({
id: data,
socketId: socket.id
});
}
// remove the active user that id is null
activeUsers = activeUsers.filter((user) => user.id !== null);
console.log(activeUsers);
io.emit('activeUsers', activeUsers);
});
socket.on("sendMessage",(data)=>{
const { receiverId } = data;
const receiver = activeUsers.find((user) => user.id === receiverId);
console.log("Sending Message to : ", receiver);
console.log(data);
if(receiver){
console.log("comming")
console.log(receiver.socketId);
// send message to the receiver
const RSocketId = receiver.socketId;
console.log("RSocketId", RSocketId);
io.to(RSocketId).emit("getMessage", {
data: data,
});
}
});
socket.on("end", () => {
console.log("end");
socket.disconnect();
});
socket.on('disconnect', () => {
console.log('User disconnected');
activeUsers = activeUsers.filter((user) => user.socketId !== socket.id);
console.log(activeUsers);
io.emit('activeUsers', activeUsers);
});
});
Client Side
import axios from "axios";
import React, { useRef } from "react";
import nookies from "nookies";
import Index from "./index";
import FriendsHolder from "../../components/Holders/FriendsHolder";
import FriendsDetails from "../../components/Holders/details-chats-holders/FriendsDetails";
import { useRouter } from "next/router";
import DmsComponent from "../../components/Holders/details-chats-holders/DmsComponent";
import { userDetails } from "../../libs/chats";
import SetUserName from "../../models/SetUserName";
import { io } from "socket.io-client";
const friends = ({ token }: any) => {
const router = useRouter();
const [isLoading, setIsLoading] = React.useState(false);
const [username, setusername] = React.useState(false);
const [sendMsg, setSendMsg] = React.useState(null);
const [receivedMsg, setReceivedMsg] = React.useState(null);
const socket = useRef<any>();
React.useEffect(() => {
const init = async () => {
const { data } = await userDetails(token);
if (
data &&
(data.data.username.length > 20 || data.data.username === "")
) {
setusername(true);
} else {
setusername(false);
socket.current = io("ws://localhost:9739");
socket?.current.emit("login", data.data?.id);
socket?.current.on("activeUsers", (users: any) => {
console.log("active users", users);
});
}
};
if (token) {
init();
}
}, []);
const handleLoading = () => {
setIsLoading(true);
};
const handleNotLoading = () => {
setIsLoading(false);
};
React.useEffect(() => {
handleNotLoading();
}, [isLoading]);
// Sending message through the socket server
React.useEffect(() => {
if (sendMsg !== null) {
socket.current?.emit("sendMessage", sendMsg);
}
}, [sendMsg]);
// Recieving Message through the socket server
React.useEffect(() => {
socket.current?.on("getMessage", (data: any) => {
setReceivedMsg(data);
});
}, []);
React.useEffect(() => {
console.log(receivedMsg);
}, [receivedMsg]);
return (
<Index>
{username ? (
<SetUserName />
) : (
<>
<FriendsHolder data={token} handleLoading={handleLoading} />
{router.asPath === "/app/friends" && <FriendsDetails token={token} />}
{router.asPath === `/app/friends?id=${router.query.id}` &&
!isLoading && (
<DmsComponent token={token} setSendMsg={setSendMsg} />
)}
</>
)}
</Index>
);
};
export default friends;
export const getServerSideProps = async (ctx: any) => {
const cookies = nookies.get(ctx);
const token = cookies.token;
if (!token) {
return {
redirect: {
destination: "/?login",
permanent: false,
},
};
}
return {
props: {
token: token || null,
},
};
};
I am using TypeScript and have Server and Client application. Below is the server code.
Server Code
import express, { Express } from "express";
import { graphqlHTTP } from "express-graphql";
import { buildSchema } from "type-graphql";
import { TaskResolver } from "./resolvers/task.resolver";
import { pgDatasource } from "./configs/db.config";
import { SeatBandingResolver } from "./resolvers/seatBanding.resolver";
import { GuestChatResolver } from "./resolvers/guestChat.resolver";
import { RateResolver } from "./resolvers/rate.resolver";
import { YearResolver } from "./resolvers/year.resolver";
import { ImplementationRateResolver } from "./resolvers/implementationRate.resolver";
import { UserResolver } from "./resolvers/user.resolver";
import { ReportResolver } from "./resolvers/report.resolver";
// Subscriptions
const ws = require("ws");
const { useServer } = require("graphql-ws/lib/use/ws");
const { execute, subscribe } = require("graphql");
const main = async () => {
const app: Express = express();
try {
//connect to db
await pgDatasource.initialize();
} catch (err) {
throw err;
}
//build gql schema
let schema = await buildSchema({
resolvers: [
SeatBandingResolver,
GuestChatResolver,
RateResolver,
YearResolver,
ImplementationRateResolver,
UserResolver,
],
validate: false,
// pubSub: new PubSub()
});
let schemaDoc = await buildSchema({
resolvers: [ReportResolver],
validate: false,
});
//ql schema for report
const docServer = graphqlHTTP((req, res) => {
return {
schema: schemaDoc,
graphiql: true,
context: {
req: req,
header: req.headers,
},
};
});
//setting a graphql server instance
const graphqServer = graphqlHTTP((req, res, graphQLParams) => {
return {
schema,
context: {
req: req,
header: req.headers,
},
graphiql: true,
};
});
app.use(cors());
//graphql endpoint : change it to backend
app.use("/graphql", graphqServer);
//for report : change name to google api
app.use("/doc", docServer);
//test route
app.get("/", (req, res) => {
res.json({
message: "Hello world",
});
});
let server = app.listen(3001, () => {
console.log("server started");
const wsServer = new ws.WebSocketServer({
host: "localhost",
// server,
path: "/graphql",
port: 3001,
});
useServer(
{
schema,
execute,
subscribe,
onConnect: (ctx) => {
console.log("Connect");
},
onSubscribe: (ctx, msg) => {
console.log("Subscribe");
},
onNext: (ctx, msg, args, result) => {
console.debug("Next");
},
onError: (ctx, msg, errors) => {
console.error("Error");
},
onComplete: (ctx, msg) => {
console.log("Complete");
},
},
wsServer
);
});
};
//starting a server
main()
.then(async (_) => {
// await addColumn()
})
.catch((err) => {
console.log(err);
});
Subscription Code at Client Side
import { Year } from "../entities/year.entity";
import { NewYear } from "../inputs/addYear.input";
import {
Arg,
Ctx,
Field,
Int,
Mutation,
ObjectType,
Query,
Resolver,
Root,
Subscription,
UseMiddleware,
} from "type-graphql";
import { Request } from "express";
import { Response } from "../helpers/response.helper";
import { Pagination } from "../inputs/pagination.input";
import { isAuth } from "../helpers/auth.helper";
import { PubSub, PubSubEngine } from "graphql-subscriptions";
const pubSub = new PubSub();
#ObjectType()
class MessagePayload {
#Field()
message: string;
}
#Resolver(() => Year)
export class YearResolver {
#Mutation(() => String)
async sendMessage(#Arg("message") message: string): Promise<string> {
console.log("in send subscription");
pubSub.publish("MESSAGE_NOTIFICATION", { message });
return message;
}
//calling the subscription
#Subscription(() => MessagePayload || null, {
topics: "MESSAGE_NOTIFICATION",
})
async receiveMessage(
#Root() root: MessagePayload
): Promise<MessagePayload | null> {
console.log("in publisher");
console.log(root, "in recieve message");
pubSub.asyncIterator("MESSAGE_NOTIFICATION");
return { message: "hello from the subscription" };
}
}
The issue I am facing here is Subscription is not working properly and the data is always null.
Can anyone help me to identify what I am missing here?
Thanks.
I'm not sure for 100% because your code descriptions are kinda confusing, but it looks like you should return pubSub.asyncIterator('MESSAGE_NOTIFICATION') in method receiveMessage. This method is called to start streaming messages to client at selected channel (MESSAGE_NOTIFICATION), not sending them. To send messages use pubsub. Of course you should change typing too.
You can find a similiar implementation here.
I just try to emit and listen socket in client and server:
As below, I only emit a message from the client to the server.
But I only get the message "connected socket" but data emit from client and server is not received.
import React, { useEffect, useState } from 'react'
import InputMainComponent from '#components/InputMainComponent'
import UserService from '../../service/user.service'
import socket from '../../socket/index.js'
import { io } from 'socket.io-client'
function InputMain() {
const [chat, setChat] = useState({
content: "",
error: "",
success: false
});
const handleChange = (e) => {
setChat({
...chat,
content: e.target.value
});
}
const handleSubmit = (idRoom, content, e) => {
e.preventDefault();
socket.connect("connection", (socket) => {
socket.emit('chat', {content: content});
socket.on("chat", (data) => {
console.log(data)
})
})
UserService.createChat(idRoom, content)
.then((res) => {
setChat({
content: "",
error: "",
success: true
});
})
.catch((error) => {
setChat({
...chat,
content: "",
error: error.message,
success: false
});
});
}
return (
<InputMainComponent handleSubmit={handleSubmit} handleOnChange={handleChange} chat={chat}/>
)
}
export default InputMain
And this is in server
//socket
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
}
});
io.on('connection', (socket) => {
console.log(`User is connected: ${socket.id}`);
socket.on("chat", (data) => {
console.log("Hi");
console.log(data);
io.sockets.emit('chat', msg)
// ...
});
socket.disconnect("disconnect", () => {
console.log(`User is disconnected: ${socket.id}`)
})
});
Finally, I only got this result below
Blockquote
Connected with MongoDB
Hi port:8080
User is connected: ixC-WnX6pzpDE82qAAAB
User is connected: kpQuAqAI2X1CraQjAAAD
User is connected: qJ69YgP4jbVN7w3TAAAF
Thanks a lot.
I'm having an issue with connecting my websocket on port 7777 to my aws deployment. I am new to sockets and deployment. I have my main port 8081 hooked up, but having issues with the websocket. I've been at this for a day and would like some pointers! Thanks!
Currently, receiving this error:
React code
import React from 'react';
import openSocket from 'socket.io-client';
let socket = openSocket('http://my-api.us-east-2.elasticbeanstalk.com:7777', { transports: ['websocket'] });
export class HomePage extends React.Component {
state = {
channels: null,
channelSelected: false,
socket: null,
typers: {},
channel: 1,
messages: [],
msg: ''
}
socket;
componentDidMount() {
}
componentDidUpdate() {
if (this.props.username && !this.state.channelSelected) {
this.configureSocket();
this.setState({ channel: 1, channelSelected: true });
this.socket.emit('joinChannel', { channel: 1, username: this.props.username });
}
}
configureSocket = () => {
socket.on('connection', () => {
if (this.state.channel) {
this.handleChannelSelect(this.state.channel.id);
}
});
socket.on('incomingChat', username => {
let typers = this.state.typers;
if (!typers[username]) {
typers[username] = 1;
this.setState({ typers })
}
});
socket.on('clearingChat', username => {
let typers = this.state.typers;
delete typers[username]
this.setState({ typers });
});
socket.on('incomingMessage', details => {
let messages = this.state.messages;
let typers = this.state.typers;
delete typers[details.username]
messages.push(details);
this.setState({ messages, typers });
})
socket.on('channel', channel => {
let channels = this.state.channels;
channels.forEach(c => {
if (c.id === channel.id) {
c.participants = channel.participants;
}
});
this.setState({ channels });
});
socket.on('message', message => {
let channels = this.state.channels
channels.forEach(c => {
if (c.id === message.channel_id) {
if (!c.messages) {
c.messages = [message];
} else {
c.messages.push(message);
}
}
});
this.setState({ channels });
});
this.socket = socket;
}
handleChannelSelect = e => {
e.preventDefault();
this.setState({ channel: 1 });
this.socket.emit('leaveChannel', this.props.username);
}
handleSendMessage = e => {
e.preventDefault();
this.socket.emit('send-message', { msg: this.state.msg, username: this.props.username, channel: this.state.channel });
this.setState({ msg: '' });
}
handleChange = e => {
const { value } = e.target;
this.setState({ msg: value });
if (!value) this.socket.emit('addingMessage', { username: this.props.username, msg: 'cleared' });
if(value) this.socket.emit('addingMessage', { username: this.props.username, msg: value });
}
render() {
return (
<div className='chat-app'>
<form onSubmit={this.handleSendMessage}>
<input type="text" onChange={this.handleChange} value={this.state.msg} />
</form>
<button onClick={this.handleChannelSelect}></button>
{ this.state.messages.length > 0 && this.state.messages.map(({ username, msg }) => <p key={Math.random()}>{`${username}: ${msg}`}</p>) }
{Object.keys(this.state.typers).length > 0 && Object.keys(this.state.typers).map(el => el !== this.props.username && <p>{`${el} typing....`}</p>)}
</div>
);
}
}
server code
const app = require("./app");
const socketio = require('socket.io');
const express = require('express');
const http = require('http');
const db = require("./db");
const AWS = require("aws-sdk");
const CryptoJS = require("crypto-js");
const em = express();
const server = http.createServer(em);
const chatPort = process.env.LIVE_CHAT_PORT || 7777;
const io = socketio(server);
server.listen(chatPort, () => {
console.log(`Live Chat sock running on port ${chatPort}`)
});
app.listen(process.env.PORT || 8081, () => {
console.log("Listening on PORT 8081");
});
io.on('connection', socket => {
let user;
socket.on('joinChannel', async ({ username, channel }) => {
user = userJoin(socket.id, username, channel);
socket.on('addingMessage', details => {
const { username, msg } = details;
if (msg === 'cleared') {
io.to(user.room).emit('clearingChat', username);
} else {
io.to(user.room).emit('incomingChat', username);
}
})
socket.join(user.room);
});
socket.on('send-message', async details => {
const { msg, username, channel } = details;
let em = await db.query(`
INSERT INTO messages(conversation_id, user_id, shared_post, message, posted)
VALUES($1, $2, null, $3, current_timestamp)
RETURNING id;
`, [channel, username, msg])
io.to(user.room).emit('incomingMessage', details)
})
socket.on('leaveChannel', username => {
const user = userLeave(username);
socket.disconnect();
});
});