audio is shown disable in twilio video chat - javascript

I am trying to create a video chat with twilio. I could turn the webcam and run the video, however i could not make the audio work. When i select the control, i get to enlarge the video and picture to picture mode but not control the audio.
This is how seen
Here is the code
function App() {
let localMediaRef = React.useRef(null);;
const [data, setIdentity] = React.useState({
identity: null,
token: null
});
const [room, setRoom] = React.useState({
activeRoom: null,
localMediaAvailable: null,
hasJoinedRoom: null
});
async function fetchToken() {
try {
const response = await fetch("/token");
const jsonResponse = await response.json();
const { identity, token } = jsonResponse;
setIdentity({
identity,
token
});
} catch (e) {
console.error("e", e);
}
}
React.useEffect(() => {
fetchToken();
}, []);
const attachTracks = (tracks, container) => {
tracks.forEach(track => {
container.appendChild(track.attach());
});
};
// Attaches a track to a specified DOM container
const attachParticipantTracks = (participant, container) => {
const tracks = Array.from(participant.tracks.values());
attachTracks(tracks, container);
};
const roomJoined = room => {
// Called when a participant joins a room
console.log("Joined as '" + data.identity + "'");
setRoom({
activeRoom: room,
localMediaAvailable: true,
hasJoinedRoom: true
});
// Attach LocalParticipant's Tracks, if not already attached.
const previewContainer = localMediaRef.current;
if (!previewContainer.querySelector("video")) {
attachParticipantTracks(room.localParticipant, previewContainer);
}
};
const joinRoom = () => {
let connectOptions = {
name: "Interview Testing"
};
let settings = {
audio: true
}
console.log('data', data, data.token)
Video.connect(
data.token,
connectOptions,
settings
).then(roomJoined, error => {
alert("Could not connect to Twilio: " + error.message);
});
};
return (
<div className="App">
<FeatureGrid>
<span onClick={joinRoom}>Webcam</span>
</FeatureGrid>
<PanelGrid>
{room.localMediaAvailable ? (
<VideoPanels>
<VideoPanel ref={localMediaRef} />
</VideoPanels>
) : (
""
)}
</PanelGrid>
</div>
);
}
export default App;
How do i enable audio too? Also the settings of video is shown only after right click. can't we show this by default?
UPDATE
its a LocalAudioTrack
this is remoteaudiotrack

Related

How to convert blobUrl to MP3 in ReactJS?

I need to take an audio recording and throw it to firebase, I do this, but the file type goes to firebase incorrectly, when it goes correctly, I cannot listen to the audio in firebase.
import {
useReactMediaRecorder,
} from "react-media-recorder";
const { status, startRecording, stopRecording, clearBlobUrl, mediaBlobUrl } =
useReactMediaRecorder({
audio: true,
blobPropertyBag: {
type: "audio/m4a",
},
});
const audioUpload = async (e: any) => {
saveMedia(reader.result?.toString(), user);
};
Firebase :
const uploadImage = async (
file: any,
user: IUser
): Promise<IUploadImage | null> => {
const storage = getStorage(firebaseApp);
const child = "1abcd/koray-test/" + idGenerator();
const imageRef = ref(storage, child);
const snapshot = await uploadToFirebase(imageRef, file);
if (!snapshot) {
return null;
}
const fbReference = snapshot.ref.fullPath;
return { fbReference, file };
};

Broadcasting to all clients with Deno websocket

I want to add notifications to an application I've developed.
Unfortunately, Deno has removed the ws package.(https://deno.land/std#0.110.0/ws/mod.ts)
That's why I'm using the websocket inside the denon itself. Since it doesn't have many functions, I have to add some things myself.
For example, sending all messages to open clients.
What I want to do is when the pdf is created, a (data, message) comes from the socket and update the notifications on the page according to the incoming data.
I keep all open clients in a Map. and when the pdf is created, I return this Map and send it to all sockets (data, message).
However, this works for one time.
server conf...
import {
path,
paths,
ctid,
} from "../deps.ts";
const users = new Map();
const sockets = new Map()
const userArr = [];
export const startNotif = (socket,req) => {
const claims = req.get("claims");
const org = req.get("org");
claims.org = org;
console.log("connected")
users.set(claims.sub, {"username":claims.sub,"socket":socket})
users.forEach((user)=>{
if(userArr.length === 0){
userArr.push(user)
}
else if(userArr.every((w)=> w.username !== user.username) )
userArr.push(user)
})
sockets.set(org, userArr)
function broadcastMessage(message) {
sockets.get(org).map((u)=>{
console.log(u.socket.readyState)
u.socket.send(message)
})
}
if (socket.readyState === 3) {
sockets.delete(uid)
return
}
const init = (msg) => {
socket.send(
JSON.stringify({
status: "creating",
})
);
};
const ondata = async (msg) => {
const upfilepath = path.join(paths.work, `CT_${msg.sid}_report.pdf`);
try {
const s=await Deno.readTextFile(upfilepath);
if(s){
socket.send(
JSON.stringify({
status: "end",
})
);
} else {
socket.send(
JSON.stringify({
status: "creating",
})
);
}
} catch(e) {
if(e instanceof Deno.errors.NotFound)
console.error('file does not exists');
}
};
const end = () => {
try {
const endTime = Date.now()
const msg = "Your PDF has been created"
const id = ctid(12) // random id create
broadcastMessage(
JSON.stringify({
id: id,
date: endTime,
status: "done",
message: msg,
read: 'negative',
action: 'pdf'
})
);
} catch (e) {
console.log(400, "Cannot send.", e);
}
}
socket.onmessage = async (e) => {
const cmd = JSON.parse(e.data);
if(cmd.bid === 'start'){
await init(cmd)
}
if(!cmd.bid && cmd.sid){
await ondata(cmd)
}
if(cmd.bid === 'end'){
await end();
}
}
socket.onerror = (e) => {
console.log(e);
};
}
client conf...
export const webSocketHandler = (request) =>
new Promise((res, rej) => {
let url;
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
url = `http://localhost:8080/api/notifications/ws`.replace('http', 'ws');
} else {
url = `${window.location.origin}/api/notifications/ws`.replace('http', 'ws');
}
const token = JSON.parse(sessionStorage.getItem('token'));
const orgname = localStorage.getItem('orgname');
const protocol = `${token}_org_${orgname}`;
const socket = new WebSocket(url, protocol);
const response = Object.create({});
socket.onopen = function () {
socket.send(
JSON.stringify({
bid: 'start',
})
);
};
socket.onmessage = function (event) {
response.data = JSON.parse(event.data);
if (response.data.status === 'creating') {
socket.send(
JSON.stringify({
sid: request.sid,
})
);
} else if (response.data.status === 'end') {
socket.send(
JSON.stringify({
bid: 'end',
})
);
} else if (response.data.status === 'done') {
try {
res(response);
} catch (err) {
rej(err);
}
}
};
socket.onclose = function (event) {
response.state = event.returnValue;
};
socket.onerror = function (error) {
rej(error);
};
});
onclick function of button I use in component...
const donwloadReport = async (type) => {
const query = `?sid=${sid}&reportType=${type}`;
const fileName = `CT_${sid}_report.${type}`;
try {
type === 'pdf' && setLoading(true);
const response = await getScanReportAction(query);
const request = {
sid,
};
webSocketHandler(request)
.then((data) => {
console.log(data);
dispatch({
type: 'update',
data: {
id: data.data.id,
date: data.data.date,
message: data.data.message,
action: data.data.action,
read: data.data.read,
},
});
})
.catch((err) => {
console.log(err);
});
if (type === 'html') {
downloadText(response.data, fileName);
} else {
const blobUrl = await readStream(response.data);
setLoading(false);
downloadURL(blobUrl, fileName);
}
} catch (err) {
displayMessage(err.message);
}
};
Everything works perfectly the first time. When I press the download button for the pdf, the socket works, then a data is returned and I update the notification count with the context I applied according to this data.
Later I realized that this works in a single tab. When I open a new client in the side tab, my notification count does not increase. For this, I wanted to keep all sockets in Map and return them all and send a message to each socket separately. But in this case, when I press the download button for the second time, no data comes from the socket.
Actually, I think that I should do the socket initialization process on the client in the context. When you do this, it starts the socket 2 times in a meaningless way.
In summary, consider an application with organizations and users belonging to those organizations. If the clients of A, B, C users belonging to X organization are open at the same time and user A pressed a pdf download button, I want A, B, C users to be notified when the pdf is downloaded.
I would be very grateful if someone could show me a way around this issue.
Have you looked at the BroadcastChannel API? Maybe that could solve your issue. See for example:
Deno specific: https://medium.com/deno-the-complete-reference/broadcast-channel-in-deno-f76a0b8893f5
Web/Browser API: https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API

Trying to figure out how to use socket.io the correct way in a useEffect that is using an axios get request to fetch messages

So far i'm stuck on my useEffect that fetches all the current messages and renders the state accordingly. as of right now it doesn't render the new state until page is refreshed.
const Home = ({ user, logout }) => {
const history = useHistory();
const socket = useContext(SocketContext);
const [conversations, setConversations] = useState([]);
const [activeConversation, setActiveConversation] = useState(null);
const classes = useStyles();
const [isLoggedIn, setIsLoggedIn] = useState(false);
const addSearchedUsers = (users) => {
const currentUsers = {};
// make table of current users so we can lookup faster
conversations.forEach((convo) => {
currentUsers[convo.otherUser.id] = true;
});
const newState = [...conversations];
users.forEach((user) => {
// only create a fake convo if we don't already have a convo with this user
if (!currentUsers[user.id]) {
let fakeConvo = { otherUser: user, messages: [] };
newState.push(fakeConvo);
}
});
setConversations(newState);
};
const clearSearchedUsers = () => {
setConversations((prev) => prev.filter((convo) => convo.id));
};
const saveMessage = async (body) => {
const { data } = await axios.post("/api/messages", body);
return data;
};
const sendMessage = (data, body) => {
socket.emit("new-message", {
message: data.message,
recipientId: body.recipientId,
sender: data.sender,
});
};
const postMessage = async (body) => {
try {
const data = await saveMessage(body);
if (!body.conversationId) {
addNewConvo(body.recipientId, data.message);
} else {
addMessageToConversation(data);
}
sendMessage(data, body);
} catch (error) {
console.error(error);
}
};
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const addMessageToConversation = useCallback(
(data) => {
// if sender isn't null, that means the message needs to be put in a brand new convo
const { message, sender = null } = data;
if (sender !== null) {
const newConvo = {
id: message.conversationId,
otherUser: sender,
messages: [message],
};
newConvo.latestMessageText = message.text;
setConversations((prev) => [newConvo, ...prev]);
}
conversations.forEach((convo) => {
console.log('hi', message.conversationId)
if (convo.id === message.conversationId) {
const convoCopy = { ...convo }
convoCopy.messages.push(message);
convoCopy.latestMessageText = message.text;
console.log('convo', convoCopy)
} else {
return convo
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
const setActiveChat = useCallback((username) => {
setActiveConversation(username);
}, []);
const addOnlineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: true };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
const removeOfflineUser = useCallback((id) => {
setConversations((prev) =>
prev.map((convo) => {
if (convo.otherUser.id === id) {
const convoCopy = { ...convo };
convoCopy.otherUser = { ...convoCopy.otherUser, online: false };
return convoCopy;
} else {
return convo;
}
}),
);
}, []);
// Lifecycle
useEffect(() => {
// Socket init
socket.on("add-online-user", addOnlineUser);
socket.on("remove-offline-user", removeOfflineUser);
socket.on("new-message", addMessageToConversation);
return () => {
// before the component is destroyed
// unbind all event handlers used in this component
socket.off("add-online-user", addOnlineUser);
socket.off("remove-offline-user", removeOfflineUser);
socket.off("new-message", addMessageToConversation);
};
}, [addMessageToConversation, addOnlineUser, removeOfflineUser, socket]);
useEffect(() => {
// when fetching, prevent redirect
if (user?.isFetching) return;
if (user && user.id) {
setIsLoggedIn(true);
} else {
// If we were previously logged in, redirect to login instead of register
if (isLoggedIn) history.push("/login");
else history.push("/register");
}
}, [user, history, isLoggedIn]);
useEffect(() => {
const fetchConversations = async () => {
try {
const { data } = await axios.get("/api/conversations");
setConversations(data);
} catch (error) {
console.error(error);
}
};
if (!user.isFetching) {
fetchConversations();
}
}, [user]);
const handleLogout = async () => {
if (user && user.id) {
await logout(user.id);
}
};
return (
<>
<Button onClick={handleLogout}>Logout</Button>
<Grid container component="main" className={classes.root}>
<CssBaseline />
<SidebarContainer
conversations={conversations}
user={user}
clearSearchedUsers={clearSearchedUsers}
addSearchedUsers={addSearchedUsers}
setActiveChat={setActiveChat}
/>
<ActiveChat
activeConversation={activeConversation}
conversations={conversations}
user={user}
postMessage={postMessage}
/>
</Grid>
</>
);
};
this is the main part im working on, the project had starter code when i began and was told not to touch the backend so i know its something wrong with the front end code. i feel like im missing something important for the socket.io
import { io } from 'socket.io-client';
import React from 'react';
export const socket = io(window.location.origin);
socket.on('connect', () => {
console.log('connected to server');
});
export const SocketContext = React.createContext();
this is how i have the socket.io setup, if anyone could point me in the right direction that would be cool. I have been reading up on socket.io as much as I can but am still pretty lost on it.
Based on the assumption the backend is working properly...
const addNewConvo = useCallback(
(recipientId, message) => {
conversations.forEach((convo) => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message);
convo.latestMessageText = message.text;
convo.id = message.conversationId;
}
});
setConversations(conversations);
},
[setConversations, conversations],
);
setConversations(conversations);
This is an incorrect way to set a state using the state's variable, and such it wont do anything. Likely why your code wont change until refresh.
Suggested fix:
const addNewConvo = useCallback(
(recipientId, message) => {
setConversations(previousState => previousState.map(convo => {
if (convo.otherUser.id === recipientId) {
convo.messages.push(message)
convo.latestMessageText = message.text;
convo.id = message.conversationId;
return convo
}
return convo
}))
},
[setConversations, conversations],
);
note: even above could be done more efficiently since I made a deep copy of messages

why componentdidmount called two times

I have React Component in componentDidMount fetch data from the server. The issue is componentDidMount called twice also the API called twice. I have a view increment API like youtube video views increment twice in the database because of twice API calling.
class SingleVideoPlay extends React.Component {
constructor(props) {
super(props);
this.player = React.createRef();
}
state = {
autoPlay: true,
relatedVideos: [],
video: null,
user: null,
comments: [],
commentInput: {
value: '',
touch: false,
error: false
},
following: false,
tab: 'comments'
};
_Mounted = false;
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
componentWillUnmount() {
this._Mounted = false;
try {
clearInterval(this.state.videoInterval);
this.props.videoEditUrl('');
} catch (error) {}
}
captureVideoTime = async () => {
const { video } = this.state;
const result = await updateWatchTime({
id: video._id,
time: 1
});
if (result.status === 200) {
const updateVideo = {
...video,
secondsWatched: video.secondsWatched + 1
};
this.setState({ video: updateVideo });
}
};
videoEnded = () => {
clearInterval(this.state.videoInterval);
};
videoPause = () => {
clearInterval(this.state.videoInterval);
};
loadVideo = () => {
clearInterval(this.state.videoInterval);
};
playingVideo = () => {
const interval = setInterval(this.captureVideoTime, 1000);
this.setState({ videoInterval: interval });
};
getVideoDetails = async (videoId) => {
const video = await getVideo(videoId);
if (video.status === 200) {
let response = video.data;
if (this.props.userId)
if (response.user._id === this.props.userId._id)
this.props.videoEditUrl(`/video/edit/${response.media._id}`);
this.setState({
relatedVideos: response.videos.docs,
video: response.media,
user: response.user
});
this.checkIsFollowing();
this.updateVideoStat(response.media._id);
}
};
updateVideoStat = async (id) => videoView(id);
checkIsFollowing = async () => {
const { userId } = this.props;
const { video } = this.state;
if (userId && video) {
const response = await isFollow({
follower: userId._id,
following: video._id
});
if (response) {
this.setState({ following: response.following });
}
}
};
addOrRemoveFollowing = async () => {
this.checkIsFollowing();
const { following, video } = this.state;
const { userId } = this.props;
if (userId) {
if (following) {
const response = await removeFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: false });
} else {
const response = await addFollow({
follower: userId._id,
following: video._id
});
this.setState({ following: true });
}
}
};
submitCommentHandler = async (event) => {
const { userId } = this.props;
event.preventDefault();
if (userId) {
const result = await saveComment({
mediaId: this.state.video._id,
parentId: '0',
userID: userId._id,
userName: userId.username,
comment: this.state.commentInput.value
});
console.log(result);
if (result.status === 200) {
this.getVideoComments();
this.setState({ commentInput: { value: '', touch: false, error: false } });
}
}
};
render() {
const { autoPlay, relatedVideos, video, user, comments, commentInput, following, tab } = this.state;
const { userId } = this.props;
return (
<div className="container-fluid">
some coponents
</div>
);
}
}
const mapStateToProps = (state) => ({
userId: state.auth.user
});
export default connect(mapStateToProps, { videoEditUrl })(SingleVideoPlay);
I don't know why componentDidMount called two times alse it shows memmory lecage issue.
How to Fix it.
Multiple componentDidMount calls may be caused by using <React.StrictMode> around your component. After removing it double calls are gone.
This is intended behavior to help detect unexpected side effects. You can read more about it in the docs. It happens only in development environment, while in production componentDidMount is called only once even with <React.StrictMode>.
This was tested with React 18.1.0
I think the issue exists on the parent component that used SingleVideoPlay component. Probably that parent component caused SingleVideoPlay component rendered more than once.
Also, there is an issue on your code.
componentDidMount() {
this._Mounted = true;
if (this._Mounted) {
const videoId = this.props.match.params.id;
this.getVideoDetails(videoId);
}
}
Here, no need to check if this._Mounted, because it will always be true.
1.Install jQuery by
npm i jquery
import $ from 'jquery'
create your function or jwuery code after the export command or put at the end of the file

send socket request from one component to different component

There is a two different component. One is patient and another is Doctor. The doctor can be on any page. When patient fires join event from his page to a particular doctor(e.g abc), the node server will then catch that event and will send JoinAccept event to 'abc doctor'. Following doctor should get notification that following patient wants to connect with you.
To achieve this, I could able to send 'Join' event up to server but could not able to listen JoinAccept event on doctor's end.
This is what I have done
context.js
import io from "socket.io-client";
import { SOCKET_URL } from "constants/url";
const SocketContext = React.createContext();
const SocketProvider = ({ children }) => {
const [socketClient, setSocketClient] = React.useState();
const [socketUpdated, setSocketUpdated] = React.useState(false);
React.useEffect(() => {
const socket = io(SOCKET_URL);
setSocketClient(socket);
return () => {
io.disconnect();
};
}, []);
React.useEffect(() => {
console.log("socketClient", socketClient);
if (socketClient) {
const tokenData =
!!localStorage.token &&
JSON.parse(atob(localStorage.token.split(".")[1]));
if (tokenData.user) {
console.log("user", tokenData.user);
socketClient.emit("clientData", tokenData.user);
socketClient.on("connected", msg => {
console.log("connected");
setSocketUpdated(true);
});
// setSocketUpdated(true);
}
}
}, [socketClient]);
console.log("socket updated", socketUpdated);
return (
<>
<SocketContext.Provider value={{ socket: socketClient, socketUpdated }}>
{children}
</SocketContext.Provider>
</>
);
};
export { SocketContext, SocketProvider };
Patient.js (it will fire Join event)
const Patient = () => {
const { socket } = React.useContext(SocketContext);
React.useEffect(() => {
const data = {
to: "abcdoctor#gmail.com",
from: "patient#gmail.com",
message: "Join a call"
};
socket.emit("Join", data);
}, [socket]);
return (
<div>
<h1>Patient</h1>
</div>
);
};
Doctor.js
const DoctorParentComponent = () => {
return (
<>
<SocketProvider>
<h1>Navbar</h1>
<DoctorRoutes />
</SocketProvider>
</>
);
};
DoctorPage.js
const DoctorPage = () => {
const [msg, setMessage] = React.useState("");
const { socket, socketUpdated } = React.useContext(SocketContext);
console.log("Doctor socket", socket, socketUpdated);
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket]);
return (
<div>
<h1>Doctor </h1>
</div>
);
};
server.js
io.of("/sockets").on("connection", socket => {
console.log("socket connection is made!!!", socket.id);
socket.on("clientData", clientData => {
console.log(clientData, "CLEITN DATA");
socket.emit("connected", "connected");
});
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
}
In my case the server gets Join event from patients and then sends event to requested doctor but doctor page is unresponsive. I mean doctor page does not listens the changes i.e it could not listens socket event JoinAccept so that it can join patient's request.
UPDATE
changing
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
socket.broadcast.to(data.to).emit("JoinAccept", msg);
});
to following works
socket.on("Join", data => {
const msg = {
message: "I am joining"
};
console.log("socket", socket.id);
console.log("I am Joining", data);
io.of("/sockets").emit("JoinAccept", msg)
});
But I want to emit 'JoinAccept' event only for a particular doctor which patient has requested for from join events.
On DoctorPage.js socket is an object, if you put this object as the only variable in the dependency array it's going to run as a loop.
Try switching to the socket id instead, so:
React.useEffect(() => {
console.log("socket", socket);
if (socket !== undefined) {
console.log("socket join", socket);
socket.on("JoinAccept", message => {
debugger;
console.log("message", message);
setMessage(message);
});
}
}, [socket.id]);

Categories

Resources