Message Unable to show automatically in reciever's side with Socket.io - javascript

I'm trying to send real time message with socket.io.
But the problem is that The reciever won't receive the message until i refresh the browser.
i'm getting all the data on the console but not in recievers end
I want to make it a real time message
Below are my codes
Messenger FrontEnd
// Context State
const { friends, setFriends, message, setMessage, authInfo } = useAuth();
const [currentFriend, setCurrentFriend] = useState("");
const [activeUser, setActiveUser] = useState([]);
const [newMessage, setNewMessage] = useState("");
const [socketMessage, setSocketMessage] = useState("");
const { updateNotification } = useNotification();
useEffect(() => {
socket.current = io("ws://localhost:9000");
socket.current.on("getMessage", (data) => {
setSocketMessage(data);
});
}, []);
useEffect(() => {
if (socketMessage && currentFriend) {
if (
socketMessage.senderId === currentFriend._id &&
socketMessage.receiverId === authInfo.profile.id
) {
console.log([...message, socketMessage]); // I'm confused on what to do here
}
}
setSocketMessage("");
}, [socketMessage]);
Socket.io Backend
let users = [];
const addUser = (userId, socketId, userInfo) => {
const checkUser = users.some((u) => u.userId === userId);
if (!checkUser) {
users.push({ userId, socketId, userInfo });
}
};
const userRemove = (socketId) => {
users = users.filter((u) => u.socketId !== socketId);
};
const findFriend = (id) => {
return users.find((u) => u.userId === id);
};
io.on("connection", (socket) => {
console.log("Socket Is Connecting...");
socket.on("addUser", (userId, userInfo) => {
addUser(userId, socket.id, userInfo);
io.emit("getUser", users);
});
socket.on("sendMessage", (data) => {
const user = findFriend(data.receiverId);
if (user !== undefined) {
socket.to(user.socketId).emit("getMessage", {
senderId: data.senderId,
senderName: data.senderName,
receiverId: data.receiverId,
createAt: data.time,
message: {
text: data.message.text,
image: data.message.image,
},
});
}
});

Related

How to get an element inside of an array in a function?

Code below outputs an array of users stored in Firestore db. Each document have the same id of a user.
const [user] = useAuthState(auth);
const [userData, setUserData] = useState([]);
const usersDB = collection(firestore, "Users");
const getUsers = async () => {
const data = await getDocs(usersDB);
setUserData(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
useEffect(() => {
getUsers();
}, []);
I want to access the document with same id as logged in user and check if isAdmin field is true or false using useAuthState to get user uid. How should I write the code for this?
According to your code you are using getDocs instead of getDoc.
Here's a sample code using useEffect and checking boolean isAdmin.
const [userData, setUserData] = useState([]);
const email = "test#xyz.com";
const password = "123Addw1113#";
const getUsers = async () => {
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
setDoc(doc(db, "<collection>", user.uid), {
someField: "<data>",
})
.then(async () => {
const docRef = doc(db, "<collection>", user.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists() && docSnap.data().isAdmin === true) {
setUserData(docSnap.data());
console.log("isAdmin is True");
} else if (docSnap.exists() && docSnap.data().isAdmin === false) {
console.log("isAdmin is False");
} else {
console.log("No such document!");
}
});
})
.catch((error) => {
console.log(error)
});
}
useEffect(() => {
getUsers();
}, []);

WebSockets server receives data but doesn't send back response

WebSocket doesn't send back a response despite the payLoad of the response is correct. The connection between front end and back end seems fine too. The boolean toggling inside the object array also works fine and does it's job. Any ideas why it isnt sending the JSON back to front end?
--------------------Front-end--------------------
const clientChangeVote = (c) => {
const payLoad = {
method: "changeVote",
clientId: gameData.clients[c].id,
gameId: gameData.id,
};
// voteValue: gameData.clients[c].voteReady,
ws.send(JSON.stringify(payLoad));
};
-----------------Back-end------------------------
if (result.method === "changeVote") {
const gameId = result.gameId;
const clientId = result.clientId;
games[gameId].clients
.filter((x) => x.id === clientId)
.forEach((vote) => (vote.voteReady = !vote.voteReady));
const updatedData = games[gameId].clients;
const payLoad = {
method: "changeVote",
updatedData: updatedData,
};
const game = games[gameId];
console.log(games);
game.clients.forEach((c) => {
console.log(payLoad);
c.connection.send(JSON.stringify(payLoad, getCircularReplacer()));
});
}
function getCircularReplacer() {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
}
-----------Inside the respone area. Im using React-----------
const [ws, setWs] = useState(new W3CWebSocket(URL));
useEffect(() => {
ws.onopen = () => {
console.log("Successful connection");
};
ws.onmessage = (message) => {
if (response.method === "changeVote") {
console.log("Vote received");
}
return () => {
ws.onclose = () => {
console.log("Connection closed");
setWs(new WebSocket(URL));
};
};
}, [ws.onmessage, ws.onopen, ws.onclose]);
Change ws.onmessage() :
ws.onmessage = (message) => {
let response = JSON.parse(message.data)
if (response.method === "changeVote") {
console.log("Vote received");
}
if not works add a comment.

Axios POST with the server responded with a status of 500 (Internal Server Error)

I'm using ReactJS to build a blog app. I can use axios get, put, delete but NOT POST. Every time I post a new blog, it gives me server responded with a status of 500 (Internal Server Error).
I have been struggle with this issue for a week and couldn't figure out the reason. Thank you very much for your help! Let me know if you need additional information.
Here are my codes:
API
import axios from 'axios'
const baseUrl = `/api/blogs`
let token = null
const setToken = newToken => {
token = `bearer ${newToken}`
}
const getAll = () => {
const request = axios.get(baseUrl)
return request.then(response => response.data)
}
const create = async newBlog => {
const config = {headers: { Authorization: token }}
const response = await axios.post(baseUrl, newBlog, config)
return response.data
}
const update = async (id, newObject) => {
const request = axios.put(`${baseUrl}/${id}`, newObject)
const response = await request
return response.data
}
const remove= async (id) => {
const config = {headers: { Authorization: token }}
const request = axios.delete(`${baseUrl}/${id}`, config)
const response = await request
return response.data
}
const exportedObject = { getAll, create, update, setToken, remove }
export default exportedObject
Frontend App.js
import React, { useState, useEffect, useRef } from 'react'
import blogService from './services/blogs'
import loginService from './services/login'
import Blog from './components/Blog'
import LoginForm from './components/LoginForm'
import BlogForm from './components/BlogForm'
import Togglable from './components/Togglable'
import Notification from './components/Notification'
import axios from 'axios'
const App = () => {
const [blogs, setBlogs] = useState([])
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [user, setUser] = useState(null)
const [loginVisible, setLoginVisible] = useState(false)
const [notificationText, setNotificationText] = useState("")
const [notificationStyle, setNotificationStyle] = useState("notification")
const [Toggle, setToggle] = useState(false)
const BlogFormRef = useRef()
useEffect(() => {
const Data = async () => {
const initialBlogs = await blogService.getAll()
setBlogs( initialBlogs )
}
Data()
}, [])
useEffect(() => {
const loggedUserJSON = window.localStorage.getItem('loggedBlogUser')
if (loggedUserJSON) {
const user = JSON.parse(loggedUserJSON)
setUser(user)
blogService.setToken(user.token)
}
}, [])
const addBlog = async (blogObject) => {
BlogFormRef.current.toggleVisibility()
if (blogObject.title !== '' && blogObject.author !== '' && blogObject.url !== '') {
const newBlog = await blogService.create(blogObject)
setBlogs(blogs.concat(newBlog))
setNotificationStyle('notification')
setNotificationText(`A new blog ${blogObject.title} by ${blogObject.author} is added`)
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
setBlogs('')
console.log(blogObject)
document.location.reload()
} else {
setNotificationStyle('Warning')
setNotificationText('You must fill all fields to create a blog')
setToggle(!Toggle)
setTimeout(() => {
setToggle(false)
}, 5000)
}
}
Backend
const blogsRouter = require('express').Router()
const Blog = require('../models/blog')
const User = require('../models/user')
const jwt = require('jsonwebtoken')
const middleware = require("../utils/middleware")
blogsRouter.get('/', async (request, response) => {
const blogs = await Blog.find({}).populate('user', { username: 1, name: 1 })
response.json(blogs)
})
blogsRouter.get('/:id', (request, response) => {
Blog.findById(request.params.id)
.then(blog => {
if (blog) {
response.json(blog)
} else {
response.status(404).end()
}
})
})
blogsRouter.post('/', middleware.userExtractor, async (request, response) => {
const body = request.body
const user = request.user
const decodedToken = jwt.verify(request.token, process.env.SECRET)
if (!decodedToken.id){
return response.status(401).json({error: 'token missing or invalid'})
}
if(body.title === undefined){
return response.status(400).send({
error: 'title is missing'
})
}
else if(body.author === undefined){
return response.status(400).send({
error: 'author is missing'
})
}
else if(body.url === undefined){
return response.status(400).send({
error: 'url is missing'
})
}
else{
const blog = new Blog({
title: body.title,
author: body.author,
url: body.url,
likes: body.likes,
user: user.id
})
const savedBlog = await blog.save()
//console.log(savedBlog)
//console.log(user)
user.blogs = user.blogs.concat(savedBlog.id)
await user.save()
const populatedBlog = await savedBlog.populate('user', { username: 1, name: 1 }).execPopulate()
response.status(200).json(populatedBlog.toJSON())
}
})
blogsRouter.delete('/:id', middleware.userExtractor, async (request, response) => {
const blog = await Blog.findByIdAndRemove(request.params.id)
const user = request.user
const decodedToken = jwt.verify(request.token, process.env.SECRET)
if(! request.token || !decodedToken.id){
return response.status(401).json({error:'token is missing or invalid'})
}
else if(blog.user.toString() === user.id.toString()){
await Blog.findByIdAndRemove(request.params.id)
response.status(204).end()
}
else{
return response.status(401).json({error:'cannot process deletion'})
}
})
blogsRouter.put('/:id', async (request, response) => {
const body = request.body
const blog = {
title: body.title,
author:body.author,
url: body.url,
likes: body.likes
}
await Blog.findByIdAndUpdate(request.params.id, blog, { new: true })
.then(updatedBlog => {
response.json(updatedBlog)
})
})
module.exports = blogsRouter
Mongoose
const mongoose = require('mongoose')
const blogSchema = new mongoose.Schema({
title: {type:String,
required: true},
author:{type:String,
required: true},
url: {type:String,
required: true},
likes: {type:Number,
default: 0},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'}
})
blogSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
}
})
const Blog = mongoose.model('Blog', blogSchema)
module.exports = Blog
Additional information: terminate output, the info is actually POST just cannot render in the front
terminal output

how to get socket.on("test", { count)) current value when component mounted?

import ...
let socket;
const Game = () => {
const ...
const [userCount, setUserCount] = useState(0);
const scrollToBottom = () => {
endToMessages.current?.scrollIntoView({ behavior: "smooth" });
};
const { _id, username } = JSON.parse(localStorage.getItem("user")).user;
useEffect(() => {
socket = io(process.env.REACT_APP_BASE_URL);
socket.emit("game_lobby", { id: _id, username, room: gameInfo.name });
return () => socket.disconnect();
}, [_id, username, gameInfo.name]);
useEffect(() => {
socket.on("message", ({ user, text }) => {
setMessages((prev) => [...prev, { user, text }]);
scrollToBottom();
});
socket.on("user_count",({count}) => {
setUserCount(count);
})
}, []);
console.log("Current user count",userCount);
const sendMessage = () => {
...
};
return (
<div className="game section">
...
</div>
);
};
export default Game;
Server side:
import { Server } from "socket.io";
const socketApi = (server) => {
const io = new Server(server, {
cors: {
origin: ["https://mook-f2b4e.web.app", "http://localhost:3000"],
methods: ["GET", "POST"],
},
});
io.on("connection", (socket) => {
socket.on("disconnect", () => {
console.log(`user ${socket.id} had left`);
});
socket.on("game_lobby", async ({ id, username, room }) => {
console.log("We have a new connetion.");
socket.join(room);
const roomClients = await (await io.in(room).fetchSockets()).length;
console.log(roomClients);
io.to(room).emit("user_count", { count: roomClients });
socket.on("disconnect", () => {
io.to(room).emit("user_count", { count: roomClients });
});
socket.emit("message", {
user: "Admin",
text: `${username} welcome to ${room} room`,
});
});
socket.on("send_message", ({ name, message, room }) => {
io.to(room).emit("message", { user: name, text: message });
});
});
return io;
};
export default socketApi;
Hi all.When I try to get user count when component mount but I can't.First time when component did mount I get 0 value. If someone else joins the room then I can get its current value. If I could explain properly I mean let's say there are 3 people in the room and I joined that room later. Now there are 4 people in the room but I got 0 value from console.log("Current user count", userCount) if the 5th person joins the room then I can get the current value from the server.
I believe you would need the server to have a variable to keep track of active users. So connection increment a variable for onlineUsers, then on disconnect subtract one from the onlineUsers
See reference here => https://stackoverflow.com/a/10276446/3124019

How to make api endpoint target user's localhost when deployed to Heroku

I have this api which works fine when running locally. But, once it is deployed to Heroku i get a error 503 which is because it tries to target localhost on Heroku's server and not the user's localhost. Is there a way to make this target the user's localhost instead?
The frontend is React. Here's the code in React that fetches this api every 5sec.
axiosFunc = () => {
const { user } = this.props.auth;
console.log(user);
axios.get(`api/avaya/${user.id}`).then((res) => console.log(res));
};
timer = (time) => {
const date = new Date(time);
return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
};
componentDidMount() {
this.axiosFunc();
this.interval = setInterval(this.axiosFunc, 5000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
and this is the API on the backend with express
const router = require("express").Router();
const xml2js = require("xml2js");
const Avaya = require("../../models/Avaya");
const User = require("../../models/User");
router.route("/:id").get(async (req, res) => {
const user = await User.findById(req.params.id);
const axios = require("axios");
axios({
method: "post",
baseURL: `http://127.0.0.1:60000/onexagent/api/registerclient?name=${user.username}`,
timeout: 2000,
})
.then((reg) => {
xml2js
.parseStringPromise(reg.data, { mergeAttrs: true })
.then((result) => {
if (result.RegisterClientResponse.ResponseCode[0] === "0") {
const clientId = result.RegisterClientResponse.ClientId[0];
user.avayaClientId = clientId;
user.save();
}
const clientId = user.avayaClientId;
axios({
method: "post",
url: `http://127.0.0.1:60000/onexagent/api/nextnotification?clientid=${clientId}`,
}).then((notification) => {
xml2js
.parseStringPromise(notification.data, { mergeAttrs: true })
.then((result) => {
const notifType = [];
const notifDetails = [];
for (let i in result.NextNotificationResponse) {
notifType.push(i);
}
const arranged = {
NotificationType: notifType[1],
ResponseCode:
result.NextNotificationResponse[notifType[0]][0],
};
for (let i in result.NextNotificationResponse[
notifType[1]
][0]) {
notifDetails.push(i);
}
for (let i = 0; i < notifDetails.length; i++) {
arranged[[notifDetails[i]][0]] =
result.NextNotificationResponse[notifType[1]][0][
notifDetails[i]
][0];
}
for (let i in arranged) {
if ("Outbound" in arranged) {
arranged.CallType = "Outbound";
} else if ("Inbound" in arranged)
arranged.CallType = "Inbound";
else {
arranged.CallType = " ";
}
}
if (
arranged.NotificationType === "VoiceInteractionCreated" ||
arranged.NotificationType === "VoiceInteractionMissed" ||
arranged.NotificationType === "VoiceInteractionTerminated"
) {
const newLogs = new Avaya({
notification: arranged,
});
newLogs.owner = user;
newLogs.save();
user.avayaNotifications.push(newLogs),
user
.save()
.then((logs) => res.json(logs))
.catch((err) => res.status(400).json("Error: " + err));
} else {
res.send("Nothing to record");
}
});
});
});
})
.catch((err) => res.status(503).json(err));
});
router.route("/history/:username").get(async (req, res) => {
const user = await User.findOne({ username: [`${req.params.username}`] });
Avaya.find({ owner: [`${await user.id}`] }).then((user) => res.json(user));
});
module.exports = router;
EDIT: I was able to fix thanks to #Molda
using fetch instead of axios doesn't result in cors error.
New frontend code
getLogs = async () => {
const { user } = this.props.auth;
const reg = await fetch(
`http://127.0.0.1:60000/onexagent/api/registerclient?name=${user.id}`
);
let regData = await reg.text();
let regxml = new XMLParser().parseFromString(regData);
if (regxml.attributes.ResponseCode === "0") {
axios.post(`/api/avaya/register/${user.id}`, regxml);
console.log(regxml.attributes.ResponseCode);
}
let resp = await fetch(`/api/avaya/getid/${user.id}`);
let clientId = await resp.text();
let logs = await fetch(
`http://127.0.0.1:60000/onexagent/api/nextnotification?clientid=${clientId}`
);
let data = await logs.text();
var xml = new XMLParser().parseFromString(data);
axios.post(`/api/avaya/getlogs/${user.id}`, xml);
};
timer = (time) => {
const date = new Date(time);
return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
};
componentDidMount() {
this.getLogs();
this.interval = setInterval(this.getLogs, 5000);
}
New backend code:
const router = require("express").Router();
const Avaya = require("../../models/Avaya");
const User = require("../../models/User");
router.route("/register/:id").post(async (req, res) => {
const user = await User.findById(req.params.id);
const clientId = req.body.attributes.ClientId;
user.avayaClientId = clientId;
user.save();
});
router.route("/getid/:id").get(async (req, res) => {
const user = await User.findById(req.params.id);
res.send(user.avayaClientId);
});
router.route("/getlogs/:id").post(async (req, res) => {
const user = await User.findById(req.params.id);
const arranged = {
NotificationType: req.body.children[0].name,
ResponseCode: req.body.attributes.ResponseCode,
CallType: " ",
};
for (let i in req.body.children[0].attributes) {
if (i === "Outbound") {
arranged.CallType = "Outbound";
}
if (i === "Inbound") {
arranged.CallType = "Inbound";
}
arranged[i] = req.body.children[0].attributes[i];
}
console.log(arranged);
if (
arranged.NotificationType === "VoiceInteractionCreated" ||
arranged.NotificationType === "VoiceInteractionMissed" ||
arranged.NotificationType === "VoiceInteractionTerminated"
) {
const newLogs = new Avaya({
notification: arranged,
});
newLogs.owner = user;
newLogs.save();
user.avayaNotifications.push(newLogs),
user
.save()
.then((logs) => res.json(logs))
.catch((err) => res.status(400).json("Error: " + err));
} else {
res.send("Nothing to record");
}
});
router.route("/history/:username").get(async (req, res) => {
const user = await User.findOne({ username: [`${req.params.username}`] });
Avaya.find({ owner: [`${await user.id}`] }).then((user) => res.json(user));
});
module.exports = router;
I really don't get the part of (requesting with Axios in API)
Is this a third party API ?
But I suggest you to use (.env) which is a file in your root folder contains the development config like base URLs, expire tokens, API keys ... etc
and when you upload to Heroku you have to make a (.env) in Heroku app and but your config
Let's take an example
in my development mode, my .env looks like
app_url = localhost:4000
port = 4000
db = development_api
db_username = root
db_password =
db_engine = mysql2
in my production mode, my .env looks like
app_url = http://appsomething.heroku.com
port = 80
db = production_api
db_username = root
db_password = 3210LDWAK#AALKQ
db_engine = mysql2
and read more about how to use .ENV

Categories