How to keep user joined to their multiple rooms socket.io - reactjs

I am completely new to socket.io and I have been stuck on this problem for so many days. I can't find any deep youtube vidoes on this topic either. What I am trying to do is, I am creating a chat app. As soon as you log in you will see all the groups and you can join these groups. Let's assume that user has joined 3 groups. As soon as that user logs in I would like to join that user to those three groups so that he would get messages when there is any. How can that be done??
This is what I have done so far,
Front-end
when message is sent
socket.emit("message", { name, message, room: selectedGroup.name });
socket.emit("join-group", { name, room: group.name });
socket.on("message", ({ name, message, room }) => {
const update = { ...group };
update.messages.push({ sender: name, message });
setSelectedGroup(update);
});
Back-End
io.on("connection", (socket) => {
socket.on("join-group", ({ name, room }) => {
socket.join(room);
});
socket.on("message", ({ name, message, room }) => {
socket.join(room);
io.to(room).emit("message", { name, message, room });
});
});

my main language is not English, so I apologize for that
There is an adapter in the io object that can access all active rooms inside, and if you want to have access to all of them when the user logs in, you must use these commands in onConnection
However, versions 2,3,4 are different in this regard.
https://socket.io/docs/v4/adapter/
From the top right you can find the version you want
const socketio = require("socket.io");
const server = http.createServer(app);
const io = socketio(server);
const activeRooms(io)=> {
const arr = Array.from(io.sockets.adapter.rooms);
// [['room1', Set(2)], ['room2', Set(2)]]
const filtered = arr.filter(room => !room[1].has(room[0]))
// ['room1', 'room2']
const res = filtered.map(i => i[0]);
return res;
}
const rooms = activeRooms()
if (rooms) {
rooms.forEach((element) => {
socket.join(element.toString());
});
socket.join(socket.username);
}
}

Related

Socket call multiple time that first give me ui with update two time then merge with original response

//this is an function that receive real time msg that other user send.
api call to get conversation list...
const handleListMyContact = useCallback(async () => {
const data = await getConversation(authToken);
if (data) {
setListData(data.info);
} else {
console.log("loading data...");
}
}, []);
useEffect(() => {
if (focus) {
handleListMyContact();
}
}, [handleListMyContact, focus]);
const receivePersonalMessage = useCallback((data: any) => {
console.log("data from emit", data);
const contactList: any = listData;
const idxObj = contactList.findIndex((object: any) => {
return object.conversation_id === data.conversation_id;
});
console.log("object find....", idxObj);
contactList.splice(idxObj, 1);
contactList.unshift(data);
console.log("contact list", contactList[0].message);
setListData((prev: any) => [contactList[0], ...prev]);
console.log("clicked...");
handleListMyContact(); // this is my api call that code is above
}, []);
//then I set socket globally. so in useEffect i direct call that socket.on instance.
useEffect(() => {
socket.on("new_conversation_message", receivePersonalMessage);
return () => {
socket.off("new_conversation_message", receivePersonalMessage);
};
}, [socket, receivePersonalMessage]);
whenever other user send the msg at the conversation tab i can see first two response then it render with original one.
// this is log i got from receivePersonalMessage function
object find.... -1
contact list Hi
clicked...
object find.... -1
contact list Hi
clicked...
and the problem is first it shows me hi msg first time in alone then marge with group but actually I want hi msg should be direct update to the group not two times in another user screen then show in orginal group. could you please help me out? I really appreciate if you could help me. Thank you in advance

Forwarding Message through Socket.io Send message 2nd time not first time

Here is my Socket connectivity and forward Message function:
const MessageForward = () => {
const token = accessToken;
socketRef.current = io.connect('http://159.203.8.120:3001', {
query: { token }
});
socketRef.current.on("profile", id => {
const conne = { profileId: id.id }
socketRef.current.emit('myProfileId', conne)
setYourID(id.id)
})
socketRef.current.on('forward-message', (message) => {
console.log("Forwarded-Message", message);
})
};
function forwardMessage(e) {
e.preventDefault();
var receiverProfileId;
if (yourID == yourID) {
receiverProfileId = selectedId
}
else {
receiverProfileId = yourID
}
const messageObjects = {
senderProfileId: yourID,
receiverProfileId: receiverProfileId,
message: message,
type: msgType,
AdId: AdId,
AdTitle: AdTitle,
AdDescription: AdDescription,
AdImage: AdImage,
username: name,
};
navigation.navigate("NewChat",
{ Id: selectedId, Name: userName, ProfileImage: selectImage })
socketRef.current.emit("forward-message", messageObjects);
};
The senderProfileId is my Id from Socket and receiverProfileId i am taking from Api by selecting user and its Id. But if i select user and forward message to selected user its not getting send but if i send it second time its getting send. Same forward-message socket i am using to get the response from the socket but first time it is not forwarding seconds time it is getting send. I want to know where i am getting struck. if anyone knows please let me know it would be appreciated.
Thankyou

Saving an ID value from an API to a User with GraphQL

I'm working on a video game website where a user can save a game to a list. How this is supposed to work is when the user clicks "Complete Game", the ID of the game is saved to a state that holds the value. The value is then passed into the mutation, then the mutation runs, saving the ID of the game to the users list of completed games. However, all I'm seeing in the console is this:
"GraphQLError: Variable \"$addGame\" got invalid value { gameId: 740, name: \"Halo: Combat Evolved\",
The above error continues, listing the entirety of the API response, instead of just the gameId.
I was able to successfully add the game to the list in the explorer with the following mutation:
mutation completeGame($addGame: AddNewGame!) {
completeGame(addGame: $addGame) {
_id
completedGameCount
completedGames {
gameId
}
}
}
with the following variable:
{
"addGame": {"gameId": 740}
}
How can I trim down what is being passed into the mutation to just be the gameId?
Below is the entirety of the page, except the return statement at the bottom.
const [selectedGame, setSelectedGame] = useState([]);
const [savedGameIds, setSavedGameIds] = useState(getSavedGameIds());
const [completeGame, { error }] = useMutation(COMPLETE_GAME);
const { id: gameId } = useParams();
useEffect(() => {
return () => saveGameIds(savedGameIds);
});
useEffect(() => {
async function getGameId(gameId) {
const response = await getSpecificGame(gameId);
if (!response.ok) {
throw new Error('Something went wrong...');
}
const result = await response.json();
const gameData = result.map((game) => ({
gameId: game.id,
name: game.name,
cover: game.cover,
summary: game.summary,
platforms: game.platforms,
platformId: game.platforms,
genres: game.genres,
genreId: game.genres,
}));
setSelectedGame(gameData);
}
getGameId(gameId);
}, [])
const handleCompleteGame = async (gameId) => {
const gameToComplete = selectedGame.find((game) => game.gameId === gameId);
const token = Auth.loggedIn() ? Auth.getToken() : null;
if (!token) {
return false;
}
try {
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
console.log(data);
setSavedGameIds([...savedGameIds, gameToComplete]);
} catch (err) {
console.error(err);
}
};
With the mutation working in the explorer when I'm able to explicitly define the variable, I am led to believe that the issue is not with the resolver or the typedef, so I'm going to omit those from this post because I don't want it to get too long.
However, I'd be happy to attach any extra code (resolver, typeDef, getSavedGameIds function, etc) if it would allow anyone to assist. The issue (I think) lies in getting my response to match the syntax I used in the explorer, which means trimming down everything except the gameId.
I specifically am extremely suspicious of this line
const gameToComplete = selectedGame.find((game) => game.gameId === gameId)
but I have fiddled around with that for awhile to no avail.
Thank you to anyone who is able to help!
It sounds like you're trying to pass more into your mutation then your schema is defined to allow. In this part:
const { data } = await completeGame({
variables: { addGame: { ...gameToComplete } },
});
You're spreading gameToComplete here which means everything in the gameToComplete object is going to be sent as a variable. If your schema is setup to just expect gameId to be passed in, but your error message is showing that name is also being passed in, you just need to adjust your variables to exclude everything you can't accept. Try:
const { data } = await completeGame({
variables: { addGame: { gameId } },
});

ReactJS: STOMP subscription to multiple topics

My React code creates a WebSocket connection to our company's ActiveMQ 5.15.5 server, and then subscribes to the following two topics: salary and decoding. The problem is that the code is only able to subscribe to one of the topics. It cannot subscribe to both.
const client = window.Stomp.client(`ws://${ipAddress}:61614`, 'aj6.stomp');
const headers = { id: 'username' };
client.debug = null;
client.connect('user', 'pass', () => {
client.subscribe(
'/topic/salary', //BREAKPOINT was set here
message => {
const body = JSON.parse(message.body);
if (body && body.pcId) {
salaries[body.pcId] = body;
setState({ salaries});
}
},
headers,
);
client.subscribe(
'/topic/decoding', //BREAKPOINT was set here
message => {
const newBody = JSON.parse(message.body);
if (newBody && newBody.PcID) {
consoleMessage[newBody.PcID] = newBody;
setState({ consoleMessage });
}
},
headers,
);
});
So in the code above I put a break-point at client.subscribe('/topic/decoding... and client.subscribe('/topic/salary.... I saw that it only subscribes to /topic/decoding but not /topic/salary.
Does anyone know how I can fix this issue so that it subscribes to both topics?
From Stomp documentation:
Since a single connection can have multiple open subscriptions with a server, an id header MUST be included in the frame to uniquely identify the subscription. The id header allows the client and server to relate subsequent MESSAGE or UNSUBSCRIBE frames to the original subscription.
Within the same connection, different subscriptions MUST use different subscription identifiers.
Stomp API definition:
subscribe(destination, callback, headers = {})
So for my understanding, You can't have the same username id for both of your subscriptions
Try creating 2 clients, e.g.:
const salaryClient = window.Stomp.client(`ws://${ipAddress}:61614`, 'aj6.stomp');
const salaryHeaders = { id: 'salary' };
salaryClient.debug = null;
salaryClient.connect('user', 'pass', () => {
salaryClient.subscribe(
'/topic/salary',
message => {
const body = JSON.parse(message.body);
if (body && body.pcId) {
salaries[body.pcId] = body;
setState({ salaries});
}
},
salaryHeaders,
);
});
const decodingClient = window.Stomp.client(`ws://${ipAddress}:61614`, 'aj7.stomp');
const decodingHeaders = { id: 'decoding' };
decodingClient.debug = null;
decodingClient.connect('user', 'pass', () => {
decodingClient.subscribe(
'/topic/decoding',
message => {
const newBody = JSON.parse(message.body);
if (newBody && newBody.PcID) {
consoleMessage[newBody.PcID] = newBody;
setState({ consoleMessage });
}
},
decodingHeaders,
);
});

React/Socket.io - Client emits to all other clients only once and then only emits to itself after that

Running into an issue with React/Socket.io. I have two different socket emitters/listeners: one for a chat, and one for keeping track live changes to the application. I have two separate windows running localhost. The issue is when i emit a change on one window, the other window can receive that change the first time but never again (i.e. get first chat message but none that follow). After that first emit/receive, the sending client starts to receive its own emitters.
front end code:
`
socket = io("localhost:3002");
componentDidMount() {
//get id from url
const { id } = this.props.match.params;
//join specific room for project
this.socket.on("connect", () => {
this.socket.emit("room", this.projectId);
});
//listener for incoming messages
this.socket.on("RECEIVE_MESSAGE", (data) => {
this.props.addChat(this.projectId, data);
});
this.socket.on("UPDATE_PROJECT", () => {
console.log("update");
this.props.fetchProject(id);
});
}
emitTaskChange = () => {
this.socket.emit("TASK_CHANGE", { data: null });
};
onChatSubmit = (e) => {
e.preventDefault();
//create object with current user as author, message, and a timestamp
const chat = {
author: this.props.currentUser.name,
message: this.state.newChat,
createdAt: new Date().toLocaleString(),
};
//send message through socket
this.socket.emit("SEND_MESSAGE", chat);
//call action creator to add new chat
this.props.addChat(this.projectId, chat);
this.setState({ currentMessage: "" });
};
handleTaskEdit = (taskId, currentStatus) => {
const newStatus = currentStatus === "todo" ? "inprogress" : "completed";
this.props.editTask(this.projectId, taskId, newStatus);
this.emitTaskChange();
};
`
backend code:
`
const io = socket(server);
//create separate chat rooms using project id
io.on("connection", (socket) => {
socket.on("room", (room) => {
socket.join(room);
socket.in(room).on("SEND_MESSAGE", (message) => {
socket.emit("RECEIVE_MESSAGE", message);
});
socket.in(room).on("TASK_CHANGE", (data) => {
socket.emit("UPDATE_PROJECT", data);
});
});
`
found the error:
had to change the server-side code from socket.on and instead use the io object that was initialized such as io.sockets.on

Resources