agora.io screen-sharing web sdk getDisplayMedia - reactjs

I'm using Agora to create screen-sharing action and I'm stuck not being able to get it to work on Safari/MacOS. The code below is working for Chrome and Firefox in both Mac/Windows. Every time I click on this IconButton, the error message is in the screenshot below.
What could be the issue? I've compared this to Agora's sample code, but cannot figure what caused this bug.
<IconButton color='primary' onClick={shareScreenClient ? clearScreenStream : startScreenStream}>
{shareScreenClient ? <StopScreenShareOutlinedIcon fontSize='large' /> : <ScreenShareOutlinedIcon fontSize='large' />}</IconButton>
//here's the def of startScreenStream.
const startScreenStream = (e) => {
e.preventDefault();
screenClient.current = AgoraRTC.createClient(agoraClientObj);
const channel = classroom;
const token = null;
screenClient.current.init(appID, () => {
screenClient.current.join(
token,
channel,
null,
(screenUid) => {
// Create the screen-sharing stream, screenStream.
setScreenStreamObj((x) => ({
...x,
streamId: screenUid,
}));
let screenStreamObj = {
streamID: screenUid,
audio: false,
video: false,
screen: true,
screenAudio: true,
};
if (audioDeviceId) screenStreamObj.microphoneId = audioDeviceId;
if (videoDeviceId) screenStreamObj.cameraId = videoDeviceId;
if (isFirefox) screenStreamObj.mediaSource = 'screen';
screenStream.current = AgoraRTC.createStream(screenStreamObj);
screenStream.current.setScreenProfile('480p_1');
screenStream.current.init(
() => {
screenStream.current.play('screen-share', { fit: 'cover' }, () => {
console.log('sharing screen');
setShareScreenClient(true);
});
screenClient.current.publish(screenStream.current, (err) => {
console.error('publish failed', err);
//clearScreenStream();
});
},
(err) => {
screenStream.current = null;
setScreenStreamObj({});
console.error('could not init stream', err);
}
);
},
(err) => {
screenClient.current = null;
console.error('Could not join channel', err);
}
);
});
}

Related

How to recover SIP js Invitation Object or Session Object in React Js on page refresh

I am implementing Audio/Video call with SIP js and Astrisk server in React JS.I was successful on creating the WebRTC Audio/Video calling. But I am facing an issue with storing the Invitation or Session Object for SIP js. Because Circular JSON data can't be stringed to store.
Assume someone has started calling and the other end got notification of calling and in that case if the page refreshed or reloaded I am unable to recover the call session to take any action(answer/ decline)
/**
* The following code is inside useState and the dependency are handled properly.
* For making it simple and sort I have just copied the required parts. */
const simpleUserDelegate = {
onCallAnswered: (session) => {
console.log(` Call answered`);
if (simpleUser) {
let remoteVideoTrack = simpleUser.getRemoteVideoTrack(session);
if (remoteVideoTrack) {
} else {
setIsAudioCall(true);
}
}
setIsCallAnswered(true);
setIsCallRecieved(false);
localStorage.setItem('isCallRecieved',null);
localStorage.setItem('callerName',null);
localStorage.setItem('callerImage',null);
setIsCallling(false);
},
onCallCreated: (session) => {
setCallSession(session);
console.log(session,` Call created`);
//console.log('session====>',JSON.stringify(session))
// localStorage.setItem('callerUserAgent',JSON.stringify(session._userAgent));
setIsCallling(true);
localStorage.getItem('callerUserAgent')
},
onCallReceived: (invitation) => {
console.log('invitation',invitation);
console.log('invitationSession',invitation.session);
setCallerActiveRoom(invitation._userAgent.options.displayRoomId);
setCallerName(invitation._userAgent.options.displayName);
setCallerImage(invitation._userAgent.options.displayImage);
localStorage.setItem('callerUserAgent',JSON.stringify(invitation.request));
console.log(` Call received`);
// dispatch(setActiveRoomId(invitation._userAgent.options.displayRoomId));
setIsCallRecieved(true);
localStorage.setItem('isCallRecieved',true);
localStorage.setItem('callerName',invitation._userAgent.options.displayName);
localStorage.setItem('callerImage',invitation._userAgent.options.displayImage);
},
onCallHangup: () => {
console.log(` Call hangup`);
setIsCallling(false);
setIsCallRecieved(false);
localStorage.setItem('isCallRecieved',null);
localStorage.setItem('callerName',null);
localStorage.setItem('callerImage',null);
setIsCallAnswered(false);
},
onCallHold: () => {
console.log(` Call hold`);
},
onRegistered: () => {
//console.log('session',session);
console.log(` Call registered`);
},
onUnregistered: () => {
console.log(` Call unregistered`);
},
onServerConnect: () => {
console.log(` server connect`);
},
onServerDisconnect: () => {
console.log(` server dis connect`);
}
};
let simpleUserOptions = {
// traceSip: false,
// logBuiltinEnabled: false,
delegate: simpleUserDelegate,
media: {
constraints: {
audio: true,
video: true
},
local: {
video: document.getElementById('localMedia')
},
remote: {
video: document.getElementById('remoteMedia'),
//audio: remoteAudioRef.current
}
},
userAgentOptions: {
logBuiltinEnabled: true,
logLevel: "debug",
authorizationPassword: password,
authorizationUsername: username,
uri: urI,
noAnswerTimeout : 30,
displayName: name,
displayImage: profileImage,
displayRoomId: `hi${displayRoomId}`
},
};
const simpleUserObj = new Web.SessionManager('wss://pbx.scinner.com:8089/ws', simpleUserOptions);
if(!simpleUserObj.isConnected()){
simpleUserObj
.connect()
.then(() => {
console.log(`${user.username} connected`);
simpleUserObj.register().then(() => {
console.log(`${user.username} registerd`);
}).catch((error) => {
alert("Failed to register.\n" + error);
});
})
.catch((error) => {
alert("Failed to connect.\n" + error);
});
setIsSARegistered(true);
setSimpleUser(simpleUserObj);
setCallerUserAgent
}else{
console.log('isconnected');
setIsSARegistered(true);
}
/**
Set calling
*/
const setCalling = (name, target) => {
simpleUser
.call(target, {
sessionDescriptionHandlerOptions: {
constraints: {
audio: true,
video: true
}
},
inviteWithoutSdp: false
}).then(() => {
console.log(`anon placed a call`);
}).catch((error) => {
console.error(`[${simpleUser.id}] failed to place call`);
console.error(error);
alert("Failed to place call.\n" + error);
});
//setIsCallling(true);
// console.log('isCallling', isCallling)
}
}
const answerCall = () => {
//callSession stored in local state
if (callSession) {
simpleUser.answer(callSession).then(() => {
console.log(`call answered`);
}).catch((error) => {
console.error(`call answered failed`);
console.error(error);
// alert("Failed to place call.\n" + error);
});
}
};

How to relaunch an electron/react application on crash

I have an application built with react and electron, here is the issue that is occurring.
Sometimes when launching the application it will have an uncaught exception because of some IPC communication that failed.
I added a handler for this in my electron entry point file according to the documentation. However, when it relaunches it does not launch MY application. It simply displays some electron template window.
Here is the handler I'm using:
process.on("uncaughtException", function (err) {
//log the message and stack trace
log.warn("uncaught Exception: ", err);
//relaunch the app
app.relaunch({ args: [] });
app.exit(0);
handleLaunch(tray, window, store, __dirname);
});
The handleLaunch function is as such:
// Library Imports
const { app, BrowserWindow, dialog } = require("electron");
const { autoUpdater } = require("electron-updater");
const log = require("electron-log");
//Functions
const { createIpcBusBridge } = require("../ipc/createIpcBusBridge");
const { createWindow } = require("../window/createWindow");
const { createTray } = require("../tray/createTray");
const handleLaunch = (tray, window, store, directoryName) => {
const isDev = !app.isPackaged;
if (isDev === true) {
const reactDevToolsId = "fmkadmapgofadopljbjfkapdkoienihi";
const {
default: installExtension,
REDUX_DEVTOOLS,
} = require("electron-devtools-installer");
installExtension([REDUX_DEVTOOLS, reactDevToolsId])
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log("An error occurred: ", err));
}
log.info("CREATING WINDOW...........");
window = createWindow(directoryName, process, store, isDev);
if (tray !== undefined) {
tray.destroy();
}
tray = createTray(window, directoryName, process);
createIpcBusBridge();
app.on("activate", function () {
/*
On macOS it's common to re-create a window in the app when the
dock icon is clicked and there are no other windows open.
*/
if (BrowserWindow.getAllWindows().length === 0)
createWindow(window, tray, directoryName, process, store);
});
if (isDev === false) {
autoUpdater.on("update-available", (_event, releaseNotes, releaseName) => {
const dialogOpts = {
type: "info",
buttons: ["Ok"],
title: "Application Update",
message: process.platform === "win32" ? releaseNotes : releaseName,
detail: "A new version is being downloaded.",
};
dialog.showMessageBox(dialogOpts, (response) => {});
});
autoUpdater.on("update-downloaded", (_event, releaseNotes, releaseName) => {
const dialogOpts = {
type: "info",
buttons: ["Restart", "Later"],
title: "Application Update",
message: process.platform === "win32" ? releaseNotes : releaseName,
detail:
"A new version has been downloaded. Restart the application to apply the updates.",
};
dialog.showMessageBox(dialogOpts).then((returnValue) => {
if (returnValue.response === 0) autoUpdater.quitAndInstall();
});
});
}
};
module.exports = { handleLaunch };
Here is the createWindow function as requested by Kiran Maniya
// Library Imports
const { BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const url = require("url");
const log = require("electron-log");
// Functions
const { handleResolutionPref } = require("./handleResolutionPref");
const { handlePositionPref } = require("./handlePositionPref");
const { maximizeWindow } = require("./maximizeWindow");
const { minimizeWindow } = require("./minimizeWindow");
const { closeWindow } = require("./closeWindow");
const createWindow = (directoryName, process, store, isDev) => {
try {
const [screenWidth, screenHeight] = handleResolutionPref(store);
const [screenXCoordinate, screenYCoordinate, isScreenPositionCustom] =
handlePositionPref(store);
let window = null;
const browserWindowOptions = {
width: screenWidth,
height: screenHeight,
frame: false,
fullscreenable: true,
resizable: true,
transparent: false,
webPreferences: {
preload: path.join(directoryName, "preload.bundle.js"),
contextIsolation: false,
nodeIntegration: true,
sandbox: false,
webSecurity: false,
},
icon: path.join(directoryName, "icon.ico"),
};
// Create the browser window.
if (isScreenPositionCustom === true) {
browserWindowOptions.x = screenXCoordinate;
browserWindowOptions.y = screenYCoordinate;
window = new BrowserWindow(browserWindowOptions);
} else {
window = new BrowserWindow(browserWindowOptions);
}
window.on("closed", () => (window = null));
if (isDev === true) {
window.loadURL("http://localhost:3000");
} else {
window.loadURL(
url.format({
pathname: path.join(directoryName, "index.html"),
protocol: "file:",
slashes: true,
})
);
}
// Handle window toggling for custom titlebar
ipcMain.on("windowMinimize", () => minimizeWindow(window));
ipcMain.on("windowMaximize", () => maximizeWindow(window));
ipcMain.on("windowClose", () => closeWindow(window));
// Open the DevTools.
// window.webContents.openDevTools()
return window;
} catch (error) {
log.info("CREATE WINDOW ERROR INSIDE FUNCTION: ", error);
}
};
module.exports = { createWindow };
Where am I going wrong here?

Why doesn't the effect get current from the link?

I need to get localMediaStream in one effect, while it is set in another effect. Please tell me why in this context it is always null (if you do not set it in the same effect), but in this case I have a duplicate userMedia. Consequences - the camera does not go out when I call track.stop(). Based on this package
const peerConnections = useRef({});
const localMediaStream = useRef(null);
const peerMediaElements = useRef({
[LOCAL_VIDEO]: null,
});
useEffect(() => {
async function handleNewPeer({peerID, createOffer}) {
if (peerID in peerConnections.current) {
return console.warn(`Already connected to peer ${peerID}`);
}
peerConnections.current[peerID] = new RTCPeerConnection({
iceServers: freeice(),
});
peerConnections.current[peerID].onicecandidate = event => {
if (event.candidate) {
socket.emit(ACTIONS.RELAY_ICE, {
peerID,
iceCandidate: event.candidate,
});
}
}
let tracksNumber = 0;
peerConnections.current[peerID].ontrack = ({streams: [remoteStream]}) => {
tracksNumber++
if (tracksNumber === 2) { // video & audio tracks received
tracksNumber = 0;
addNewClient(peerID, () => {
if (peerMediaElements.current[peerID]) {
peerMediaElements.current[peerID].srcObject = remoteStream;
} else {
// FIX LONG RENDER IN CASE OF MANY CLIENTS
let settled = false;
const interval = setInterval(() => {
if (peerMediaElements.current[peerID]) {
peerMediaElements.current[peerID].srcObject = remoteStream;
settled = true;
}
if (settled) {
clearInterval(interval);
}
}, 1000);
}
});
}
}
/*localMediaStream.current = await navigator.mediaDevices.getUserMedia({
audio: audio,
video: video
})*/
localMediaStream.current.getTracks().forEach(track => { // localMediaStream null
peerConnections.current[peerID].addTrack(track, localMediaStream.current);
});
if (createOffer) {
const offer = await peerConnections.current[peerID].createOffer();
await peerConnections.current[peerID].setLocalDescription(offer);
socket.emit(ACTIONS.RELAY_SDP, {
peerID,
sessionDescription: offer,
});
}
}
socket.on(ACTIONS.ADD_PEER, handleNewPeer);
return () => {
socket.off(ACTIONS.ADD_PEER);
}
}, []);
// The installation, everything is as in the source, it did not work until I added the crutch above, but when it came to stopping the video stream, a bug appeared with the camera always on
useEffect(() => {
async function startCapture() {
console.log('start capture');
localMediaStream.current = await navigator.mediaDevices.getUserMedia({
audio: audio,
video: video
}).catch(console.log);
addNewClient(LOCAL_VIDEO, () => {
const localVideoElement = peerMediaElements.current[LOCAL_VIDEO];
if (localVideoElement) {
localVideoElement.volume = 0;
localVideoElement.srcObject = localMediaStream.current;
}
});
}
startCapture().then((data) => socket.emit(ACTIONS.JOIN, {room: roomID})).catch((e) => console.error(e)).finally(() => console.log('finally'));
console.log(roomID);
return () => {
localMediaStream.current.getTracks().forEach(track => track.stop());
socket.emit(ACTIONS.LEAVE);
};
}, [roomID]);
Thanks you very much.

Method “props” is meant to be run on 1 node. 0 found instead

I working on Testcases:
when i am running individual testcases it working fine but altogether if i am running its throwing error for 2 testscenarios.
my tsx code:
const onPasswordChange = useCallback((e) => {
setStatusAlert({});
setModel({ ...model, "password": e.target.value });
setPasswordError(null);
}, [setStatusAlert, setModel, setPasswordError]);
const showPasswordInput = useCallback((e) => {
e.preventDefault();
const emailIsEmpty = !model.email || model.email.length < 1;
setDisplayPasswordInput(!emailIsEmpty);
setEmailError(emailIsEmpty ? "Email is required" : null);
}, [model, setDisplayPasswordInput, setEmailError]);
My testcases:
describe("onPasswordChange", () => {
it("sets password to new value", async () => {
const expectedPassword = lorem.word();
mockedHttpHelper.get.mockImplementationOnce(jest.fn((params: IGetParams) => params.onGetSuccess({ data: { externalTypes: [] } } as AxiosResponse)));
await usingMount(
LoginFormComponent(defaultModelWithEmail),
async wrapper => {
await setTimeoutPromise(() => {
wrapper.find(LoginForm).find("LoginPasswordFormBody").invoke("onPasswordChange")({ target: { name: "password", value: expectedPassword } });
expect(wrapper.find(LoginForm).find("LoginPasswordFormBody").prop("model")).toEqual(expect.objectContaining({ password: expectedPassword }));
}, 100);
}
);
});
it("empties status alert", async () => {
const expectedError = lorem.word();
mockedHttpHelper.get.mockImplementationOnce(jest.fn((params: IGetParams) => params.onGetSuccess({ data: { externalTypes: [] } } as AxiosResponse)));
await usingMount(
<GrabStatusAlertContext>
<LoginForm model={defaultModelWithEmail} />
</GrabStatusAlertContext>,
async wrapper => {
await setTimeoutPromise(() => {
statusAlertContext.setStatusAlert({ color: "danger", text: expectedError });
expect(statusAlertContext.statusAlert).toEqual({ color: "danger", text: expectedError });
wrapper.find(LoginForm).find("LoginPasswordFormBody").invoke("onPasswordChange")({ target: { name: "password", value: expectedError } });
wrapper.update();
expect(statusAlertContext.statusAlert).toEqual({});
}, 100);
}
);
});
it("sets passwordError to null", async () => {
const passwordRequired = lorem.word();
mockedHttpHelper.get.mockImplementationOnce(jest.fn((params: IGetParams) => params.onGetSuccess({ data: { externalTypes: [] } } as AxiosResponse)));
await usingMount(
<GrabStatusAlertContext>
<LoginForm model={defaultModelWithEmail} />
</GrabStatusAlertContext>,
async wrapper => {
await setTimeoutPromise(() => {
// trigger passwordError by submitting an empty input and verify passwordError is not null
wrapper.find("form").simulate("submit");
expect(wrapper.find(LoginForm).find("LoginPasswordFormBody").prop("passwordError")).toEqual("Password is required");
wrapper.find(LoginForm).find(`input[type="password"]`).simulate("change", { target: { name: "password", value: passwordRequired } });
wrapper.update();
expect(wrapper.find(LoginForm).find("LoginPasswordFormBody").prop("passwordError")).toEqual(null);
}, 100);
}
);
});
});
Please help me any way i can resolve this issue.
I had the same problem i just gave the element an #id and used it in the find() function

React-Apollo: Recommended way of subscribing to multiple events that doesn't require UI updates

So i want to subscribe to multiple events for the current logged user.
I've extracted the subscriptions to a separate function that update my logged user state from inside and returns an array of subscriptions.
Now i wanted to know is there a different / better way of doing this ?
Is this the correct / recommended way of approaching this problem ?
Current implementation
export const subscribeToCurrentUserUpdates = (setLoggedUser) => {
const friendRequestObserver$ = apolloClient.subscribe(
{ query: queries.NEW_FRIEND_REQUEST },
);
const followersUpdatesObserver$ = apolloClient.subscribe(
{ query: queries.FOLLOWERS_UPDATES },
);
const acceptedFriendRequestObserver$ = apolloClient.subscribe(
{ query: queries.ACCEPTED_FRIEND_REQUEST },
);
const friendRequestSubscription = friendRequestObserver$.subscribe({
next: ({ data: { newFriendRequest } }) => {
Alert.success(`${newFriendRequest.username} just sent you a friend request`);
setLoggedUser((loggedUser) => {
loggedUser.incomingFriendRequests.unshift(newFriendRequest._id);
});
},
error: err => console.error(err),
});
const followersUpdatesSubscription = followersUpdatesObserver$.subscribe({
next: ({ data: { followersUpdates: { follower, isFollow } } }) => {
if (isFollow) {
Alert.success(`${follower.username} is now following you`);
}
setLoggedUser((loggedUser) => {
isFollow
? loggedUser.followers.unshift(follower._id)
: loggedUser.followers.splice(loggedUser.followers.indexOf(follower._id), 1);
});
},
error: err => console.error(err),
});
const acceptedFriendRequestSubscription = acceptedFriendRequestObserver$.subscribe({
next: ({ data: { acceptedFriendRequest: newFriend } }) => {
Alert.success(`${newFriend.username} just accepted your friend request!`);
setLoggedUser((loggedUser) => {
loggedUser.friends.push(newFriend._id);
loggedUser.sentFriendRequests.splice(
loggedUser.sentFriendRequests.indexOf(newFriend._id), 1,
);
});
},
error: err => console.error(err),
});
return [
friendRequestSubscription,
followersUpdatesSubscription,
acceptedFriendRequestSubscription,
];
};
The way i subscribe from my component
const App = () => {
const currentUserSubscriptionRef = useRef();
useEffect(() => {
if (loggedUser && !currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current = subscribeToCurrentUserUpdates(
setLoggedUser,
);
}
if (!loggedUser && currentUserSubscriptionRef.current) {
currentUserSubscriptionRef.current.forEach((subscription) => {
subscription.unsubscribe();
});
currentUserSubscriptionRef.current = null;
}
}, [loggedUser, setLoggedUser]);
}

Resources