How to send data between caller and receiver in PeerJS? - meteor

I am building audio calling app and I am struggling to send data between the call receiver to the caller. Here is my code (In the call receiver):
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
peer.on('call', function(call) {
console.log("call.peer: " + call.peer)
conn = peer.connect(call.peer)
bootbox.dialog({
className: "modal-danger nonumpad",
closeButton: false,
animate: true,
title: 'Call Recieved',
message: "Accept or Decline",
onEscape: null,
buttons: {
pickup: {
label: "<i class=\"fa fa-phone\"></i> Answer",
className: "btn-warning btn-lg pull-left",
callback: function(){
conn.send('ACCEPT') // send Accept to the caller
return false
}
},
hangup: {
label: "<i class=\"fa fa-phone\"></i> Decline",
className: "btn-warning btn-lg pull-left",
callback: function(){
conn.send('DECLINED') // Send Decline to the caller
return false;
}
}
}
});
});
With the above code, when I make the call, dialog appears and when I press one of the options, data should be sent to the caller.
Here is the caller code, who receives the above sent data:
peer.on('open', function () {
Meteor.users.update(Meteor.userId(), { $set: { 'profile.peerId': peer.id } })
console.log(Meteor.user().profile.peerId);
peer.on('connection', function(conn) {
conn.on('data', function(data){
console.log(data);
});
});
});
Nothing is printed to the console.
What am I doing wrong here?

The connection event isn't important. You want to listen to the call event like this
peer.on('call', function (incomingCall) {
Hopefully that will work better for you.
I wouldn't update the user record with the peerId, Meteor don't recommend using it, as the authentication system also writes to it. I have used dburles:presence successfully, and in fact passed the presence id to peerjs to use (rather than letting peerjs allocate one)

Related

ExtJS 6.01 How to update tpl after store loaded

Let's say I have a component with tpl in HTML format, which has a variable named testname, default value is null.
A listener of beforerender calls to a function in viewController, and in this function, I need to load a store and a callback function to update the variable testname in the view, based on the store record returned.
The problem I met is the order of code execution. The function in the viewController always ended first before the store.load callback get executed. Therefore the front view will never get updated. I have tried to setup the asynchronousLoad to false but it doesn't help.
Here is the code in the viewController.
onViewRendering: function(theView) {
console.log('setp 1');
var theStore = Ext.create('App.store.Test');
theStore.load({
asynchronousLoad: false,
callback: function(record) {
console.log('setp 2');
if (record) {
theView.data.testname = 'updated';
}
}
});
console.log('setp 3');
}
Console log displays in step 1, 3, 2.
And here is the code in the View:
Ext.define('App.view.TestView', {
extend: 'Ext.component',
data:{
testname:null
},
tpl:'<p>{testname}</p>',
listeners: {
beforerender: 'onViewRendering'
}
})
Here is the store code:
Ext.define('App.store.Test', {
extend: 'Ext.data.Store',
alias: 'store.test',
autoLoad: true,
remoteFilter: true,
fields: ['id', 'name'],
proxy: {
type: 'direct',
directFn: 'test.getTest'
}
})
I am new to Extjs and really need some help here, thanks in advance!
To update tpl after store load, you have to call setData method like below:
Code snippet:
onViewRendering: function(theView) {
console.log('setp 1');
var theStore = Ext.create('App.store.Test');
theStore.load({
asynchronousLoad: false,
callback: function(record) {
console.log('setp 2');
if (record) {
theView.setData({testname: 'updated'}); //setData method
}
}
});
console.log('setp 3');
}

How to send confirmation messages between clients - PeerJS

I am using PeerJS to make audio calls between clients and it works. I am showing a Dialog to the user who is receiving a call with two buttons (Answer, and Decline). As shown below:
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
peer.on('call', function(call) {
// sweetAlert("BINGO")
bootbox.dialog({
className: "modal-danger nonumpad",
closeButton: false,
animate: true,
title: 'Call Recieved',
message: "Accept or Decline",
onEscape: null,
buttons: {
pickup: {
label: "<i class=\"fa fa-phone\"></i> Answer",
className: "btn-warning btn-lg pull-left",
callback: function(){
return false
}
},
hangup: {
label: "<i class=\"fa fa-phone\"></i> Decline",
className: "btn-warning btn-lg pull-left",
callback: function(){
return false;
}
}
}
});
});
My question here is, how can I send the action to the other user (who is making the call)?
The reason is because I want to show timer once the receiver press on "Answer" or show dialog to inform the caller that the receiver has pressed decline
From the PeerJS documentation:
Data connections
Connect
var conn = peer.connect('another-peers-id');
conn.on('open', function(){
conn.send('hi!');
});
Receive
peer.on('connection', function(conn) {
conn.on('data', function(data){
// Will print 'hi!'
console.log(data);
});
});
You can get the id of the peer making the call via call.peer and then open a data connection to send a message ('accept', 'decline', etc) to the caller

Calling setState in render is not avoidable

React document states that the render function should be pure which mean it should not use this.setState in it .However, I believe when the state is depended on 'remote' i.e. result from ajax call.The only solution is setState() inside a render function
In my case.Our users can should be able to log in. After login, We also need check the user's access (ajax call )to decide how to display the page.The code is something like this
React.createClass({
render:function(){
if(this.state.user.login)
{
//do not call it twice
if(this.state.callAjax)
{
var self=this
$.ajax{
success:function(result)
{
if(result==a)
{self.setState({callAjax:false,hasAccess:a})}
if(result==b)
{self.setState({callAjax:false,hasAccess:b})}
}
}
}
if(this.state.hasAccess==a) return <Page />
else if(this.state.hasAccess==a) return <AnotherPage />
else return <LoadingPage />
}
else
{
return <div>
<button onClick:{
function(){this.setState({user.login:true})}
}>
LOGIN
</button>
</div>
}
}
})
The ajax call can not appear in componentDidMount because when user click LOGIN button the page is re-rendered and also need ajax call .So I suppose the only place to setState is inside the render function which breach the React principle
Any better solutions ? Thanks in advance
render should always remain pure. It's a very bad practice to do side-effecty things in there, and calling setState is a big red flag; in a simple example like this it can work out okay, but it's the road to highly unmaintainable components, plus it only works because the side effect is async.
Instead, think about the various states your component can be in — like you were modeling a state machine (which, it turns out, you are):
The initial state (user hasn't clicked button)
Pending authorization (user clicked login, but we don't know the result of the Ajax request yet)
User has access to something (we've got the result of the Ajax request)
Model this out with your component's state and you're good to go.
React.createClass({
getInitialState: function() {
return {
busy: false, // waiting for the ajax request
hasAccess: null, // what the user has access to
/**
* Our three states are modeled with this data:
*
* Pending: busy === true
* Has Access: hasAccess !== null
* Initial/Default: busy === false, hasAccess === null
*/
};
},
handleButtonClick: function() {
if (this.state.busy) return;
this.setState({ busy: true }); // we're waiting for ajax now
this._checkAuthorization();
},
_checkAuthorization: function() {
$.ajax({
// ...,
success: this._handleAjaxResult
});
},
_handleAjaxResult: function(result) {
if(result === a) {
this.setState({ hasAccess: a })
} else if(result ===b ) {
this.setState({ hasAccess: b })
}
},
render: function() {
// handle each of our possible states
if (this.state.busy) { // the pending state
return <LoadingPage />;
} else if (this.state.hasAccess) { // has access to something
return this._getPage(this.state.hasAccess);
} else {
return <button onClick={this.handleButtonClick}>LOGIN</button>;
}
},
_getPage: function(access) {
switch (access) {
case a:
return <Page />;
case b:
return <AnotherPage />;
default:
return <SomeDefaultPage />;
}
}
});

Meteor subscription won't set checkbox if app is refreshed

I'm running into an issue with Meteor subscription not setting checkbox "checked" after refresh. Basically, if I have Meteor running and change the JS, the app works as expected (pulls data from Meteor.user() and sets checkbox accordingly). However, if I refresh the app all my checkboxes are set to false. I don't see why that should happen, any ideas?
My subscription looks as follows:
Server-side
Meteor.publish("user-preferences", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'notification': 1}});
} else {
this.ready();
}
});
Client-side:
Meteor.subscribe("user-preferences", function() {
if (Meteor.user()) {
var notification = Meteor.user().notification;
// Check to see if notification and systems exists; if not, default to TRUE
if (notification) {
Session.set('aNotificationPreference', notification.hasOwnProperty('a') ? notification.a : true);
}
else {
Session.set('aNotificationPreference', true);
}
}
else {
// TRUE too if no user yet
Session.set('aNotificationPreference', true);
}
});
This is the helper that will look up the session variable to make things reactive:
Template.preferences.helpers({
isChecked: function() {
console.log('#### PREF INITIAL: ' + Session.get("aNotificationPreference"));
var pref = Session.get("aNotificationPreference");
if (typeof pref === 'undefined') {
Meteor.setTimeout(function() {
pref = Session.get("aNotificationPreference");
console.log('#### PREF IN TIMEOUT: ' + pref);
return pref ? 'checked' : false;
}, 1250);
}
else {
console.log('#### PREF in ELSE: ' + pref);
return pref ? 'checked' : false;
}
}
});
Finally, this is the HTML checkbox field:
<input type="checkbox" data-role="flipswitch" data-theme="b" name="aNotificationSwitch" id="aNotificationSwitch" data-mini="true" checked={{isChecked}}>
This is based of the Blaze documentation on this specifically and other posts I found.
I know the issue is in the helper but I'm not sure how to address it. The logs on failure show as follows:
Application Cache Checking event (index):1
Application Cache NoUpdate event (index):1
#### PREF INITIAL: undefined index.js?8b2a648142fb63b940f4fb04771d18f25b5bf173:63
Connected to Meteor Server. index.js?8b2a648142fb63b940f4fb04771d18f25b5bf173:37
#### PREF INITIAL: true index.js?8b2a648142fb63b940f4fb04771d18f25b5bf173:63
#### PREF in ELSE: true index.js?8b2a648142fb63b940f4fb04771d18f25b5bf173:73
Connected to Meteor Server. index.js?8b2a648142fb63b940f4fb04771d18f25b5bf173:37
#### PREF IN TIMEOUT: true
I am not an expert in Meteor yet.
But I would change the JavaScript you have for:
if(Meteor.isClient){
Template.preferences.helpers({
isChecked: function() { return Meteor.user().notification ? 'checked' : false; }
});
Template.preferences.events({
'click #aNotificationSwitch': function(event){
var checked = event.target.checked; // true or false
Meteor.users.update({_id: Meteor.userId()}, $set: {notification: checked});
}
});
}
It's different from your approach though. With this way you don't need to use session variables.
Allowing to update users:
Meteor.users.allow({
update: function(userId, user){
return user._id == userId;
}
});
The best solution I found was to set the flipswitch with jQueryMobile by doing the following on my panel's open event in the Template.body.events:
'panelbeforeopen #mypanel': function() {
//Refresh flipswitches prior to opening sidepanel otherwise they default to disabled
$("#aNotificationSwitch").flipswitch("enable").flipswitch("refresh");
$("#bNotificationSwitch").flipswitch("enable").flipswitch("refresh");
$("#cNotificationSwitch").flipswitch("enable").flipswitch("refresh");
}

Unable to pass result to router after calling Meteor.methods

I encounter an error using Meteor. I call an Method.method.
Template.WelcomeTemplate.events({
'click #btn-findgame': function(e) {
e.preventDefault();
console.log('clicked find game button');
Meteor.call('allocateGame', function(error, id) {
if (error) {
alert(error.reason);
} if (id) {
Router.go('gameRoom', {_id: id})
}
})
}
})
With my Method, I check if there is an room available, create one when the isn't otherwise join. And return the ID of this room.
Meteor.methods({
allocateGame: function () {
console.log('allocateGame method called')
var user = Meteor.user();
// find game where one player is in the room
var gameWaiting = Games.findOne({players: {$size: 1}})
if (!gameWaiting) {
console.log('no game available, create a new one');
var newGameId = Games.insert({players: [user._id], active: false, finished: false});
GameDetails.insert({gameId: newGameId, gameData: []});
return newGameId
} else {
if (_.contains(gameWaiting.players, user._id)) {
console.log('Cannot play against yourself sir')
} else {
console.log('Joining game');
Games.update({_id: gameWaiting._id}, {
$set: {active: true},
$push: {players: user._id}
});
return gameWaiting._id;
}
};
}
})
And my Router:
Router.map(function () {
this.route('welcome', {
path: '/',
controller: WelcomeController})
this.route('gameRoom', {
path: '/game/_:id'
})
});
The Error I recieve is:
Exception in delivering result of invoking 'allocateGame': TypeError: Cannot read property 'charAt' of null
at Object.IronLocation.set (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:1293:12)
And indeed, If I don't return an ID the Routing will continue as normal. However when I return an ID in my WelcomeTemplate an error will occur.
EDIT:
Even thought my MongoDB is updating my MiniMongo DB is empty. There must be a problem with syncing. Any idea where to look?
In the route, you set the path to be '/game/_:id', that is, a parameter with the name id. In your call to Router.go, you pass a parameter with the name _id.
Don't know if this solves your problem, but it's an error.
This kind of embarrassing taking in account how many hours I've spent on fixing this. The error was created because of an error in my routers.js
this.route('gameRoom', {
path: '/game/_:id'
})
Should be:
this.route('gameRoom', {
path: '/game/:_id'
})
Happy coding.

Resources