Differentiate Initiator & Participants in Opentok - angularjs

I am using Node + Angular to create Multiple Video Chat. Creating sessions and tokens on server side and use them to connect with others in client side. I have defined the userType while generating Token. Now, what I want is that only 'organizers' will be able to initiate the video and 'users' can only subscribe to the video. I am able to differentiate it on the level of subscribers, but not in publishers.
Here : streamCreated() is working well but sessionConnected() is not working as I am not able to put it into subContainer
var publisher = OT.initPublisher('publisher',{name:'MyGroup'});
// Attach event handlers
session.on({
// This function runs when session.connect() asynchronously completes
sessionConnected: function(event) {
// Publish the publisher we initialzed earlier (this will trigger 'streamCreated' on other
// clients)
if(JSON.parse(session.connection.data).userType === 'organizer'){
var subContainer = document.createElement('div');
document.getElementById('initiator-container').appendChild(subContainer);
}else{
var subContainer = document.createElement('div');document.getElementById('publisher').appendChild(subContainer);
}
session.publish(publisher,subContainer);
},
// This function runs when another client publishes a stream (eg. session.publish())
streamCreated: function(event) {
// Create a container for a new Subscriber, assign it an id using the streamId, put it inside
if(JSON.parse(event.stream.connection.data).userType == 'organizer'){
// set it as initiator
// initiator-container
var subContainer = document.createElement('div');
subContainer.id = 'stream-' + event.stream.streamId;
document.getElementById('initiator-container').appendChild(subContainer);
var subscriberProperties = {height: 486,width:'100%'};
// Subscribe to the stream that caused this event, put it inside the container we just made
session.subscribe(event.stream, subContainer,subscriberProperties);
}else{
// participants
var subContainer = document.createElement('div');
subContainer.id = 'stream-' + event.stream.streamId;
document.getElementById('subscribers').appendChild(subContainer);
// Subscribe to the stream that caused this event, put it inside the container we just made
session.subscribe(event.stream, subContainer);
}
},
streamDestroyed: function(event){
console.log("Stream " + event.stream.name + " ended. " + event.reason);
},
// stream property changed
streamPropertyChanged: function(event){
console.log(event)
}
});
// Connect to the Session using the 'apiKey' of the application and a 'token' for permission
session.connect(token);
Anyone with possible solution?

I'm not sure exactly what you're trying to do but it seems like you want to put the Publishers and Subscribers in different containers depending on what the userType is in the connection data.
The issue is doing this in the session.publish call but it's actually the initPublisher call that is inserting the publisher. I think you want to wait and create your Publisher after you are connected so that you know what type of user you are. So move the initPublisher call into sessionConnected like this:
session.on('streamCreated', function(event) {
var elementId = JSON.parse(session.connection.data).userType === 'organizer' ? 'publisher' : 'initiator-container';
var publisher = OT.initPublisher(elementId, {insertMode: 'append'});
session.publish(publisher);
});
Also insertMode is your friend. It will make inserting these elements a bit easier.

Related

Send Notification properly using Socket.io Redis server

OBJECTIVE: When post is added to perticular channel send notification to all connected subscribed users for that channel.
Relational Database tables over view: Post, Channels, Users, Channels_Users
Post: id, title, content, channel_id(Indicate post is related to what channel)
Channels: id, name (List of Channels)
Users: id, username (User Table)
Channels_Users: id, channel_id, user_id (This table indicate which channels user is subscribed to.)
Goal: When post is created, send notification to perticular user group.
I have tried serveral ways and it is working as aspected. But I am looking for the correct way of doing this.
Server Side: socket.js
Naming conventions:
Channel name:
channel-1, channel-2, channel-3, ....
Namespace name('namespace-'+[channelName]) :
namespace-channel-1, namespace-channel-2, ...
var app = require('express')();
var request = require('request');
var http = require('http').Server(app);
var io = require('socket.io').listen(http);
var Redis = require('ioredis');
var redis = new Redis(####, 'localhost');
// Get all channels from Database and loop through it
// subscribe to redis channel
// initialize namespace inorder to send message to it
for(var i=1; i=<50; i++) {
redis.subscribe('channel-'+i);
io.of('/namespace-channel-'+i).on('connection', function(socket){
console.log('user connected');
});
}
// We have been subscribed to redis channel so
// this block will hear if there is any new message from redis server
redis.on('message', function(channel, message) {
message = JSON.parse(message);
// send message to specific namespace, with event = newPost
io.of('/namespace-'+channel).emit('newPost', message.data);
});
//Listen
http.listen(3000, function(){
console.log('Listening on Port 3000');
});
Client side: Angularjs Socket factory code: (Mentioning main socket code)
// userChannels: user subscribed channels array
// Loop through all channel and hear for new Post and
// display notification if there is newPost event
userChannels.forEach(function (c) {
let socket = {};
console.info('Attempting to connect to namespace'+c);
socket = io.connect(SOCKET_URL + ':3000/namespace-'+c, {query: "Authorization=token"});
socket.on('connect',function(){
if(socket.connected){
console.info("namespace-" + c + " Connected!");
// On New post event for this name space
socket.on('newPost', function(data) {
/* DISPLAY DESKTOP NOTIFICATION */
});
}
});
});
Publish data to Redis server, at the time of new post created
$data = [
'event' => 'newPost',
'data' => [
'title' => $postTitle,
'content' => $postContent
]
];
$redisChannel = "channel-" . $channelId; // As per our naming conventions
Redis::publish($redisChannel, json_encode($data));
The user is getting notification correctly for the channels they have been subscribed.
Problem 1: I am not sure this is the best solution to implement this notification thing.
Problem 2: When a user opens the same application in more than one browser tabs, it gets the notification for all of them. It should send only one notification. This is something related to user management at server side on redis end. I am not sure about this part too.
I really appreciate your suggestion/help on this. Thank you in advance.

Firebase: How can i use onDisconnect during logout?

How can i detect when a user logs out of firebase (either facebook, google or password) and trigger the onDisconnect method in the firebase presence system. .unauth() is not working. I would like to show a users online and offline status when they login and out, minimize the app (idle) - not just when the power off their device and remove the app from active applications on the device.
I'm using firebase simple login for angularjs/ angularfire
Im using code based off of this tutorial on the firebase site.
https://www.firebase.com/blog/2013-06-17-howto-build-a-presence-system.html
Please i need help with this!
Presence code:
var connectedRef = new Firebase(fb_connections);
var presenceRef = new Firebase(fb_url + 'presence/');
var presenceUserRef = new Firebase(fb_url + 'presence/'+ userID + '/status');
var currentUserPresenceRef = new Firebase(fb_url + 'users/'+ userID + '/status');
connectedRef.on("value", function(isOnline) {
if (isOnline.val()) {
// If we lose our internet connection, we want ourselves removed from the list.
presenceUserRef.onDisconnect().remove();
currentUserPresenceRef.onDisconnect().set("<span class='balanced'>☆</span>");
// Set our initial online status.
presenceUserRef.set("<span class='balanced'>★</span>");
currentUserPresenceRef.set("<span class='balanced'>★</span>");
}
});
Logout function:
var ref = new Firebase(fb_url);
var usersRef = ref.child('users');
service.logout = function(loginData) {
ref.unauth();
//Firebase.goOffline(); //not working
loggedIn = false;
seedUser = {};
clearLoginFromStorage();
saveLoginToStorage();
auth.logout();
};
The onDisconnect() code that you provide, will run automatically on the Firebase servers when the connection to the client is lost. To force the client to disconnect, you can call Firebase.goOffline().
Note that calling unauth() will simply sign the user out from the Firebase connection. It does not disconnect, since there might be data that the user still has access to.
Update
This works for me:
var fb_url = 'https://yours.firebaseio.com/';
var ref = new Firebase(fb_url);
function connect() {
Firebase.goOnline();
ref.authAnonymously(function(error, authData) {
if (!error) {
ref.child(authData.uid).set(true);
ref.child(authData.uid).onDisconnect().remove();
}
});
setTimeout(disconnect, 5000);
}
function disconnect() {
ref.unauth();
Firebase.goOffline();
setTimeout(connect, 5000);
}
connect();

SocketIO syncUpdate model for different user

I have been writing an web app, where individual user will post their task and will be displayed on their news feed. I have used socket io with angular fullstack framework by yeoman.
what I am trying to do is, when user adds new data to goal model, on socketSyncUpadtes, $rootScope.getUserGoals get updated and user see new entry. but I want that to happen for each user, thats why I included userid, to make sure, it only pulls user specific data.
But in reality what is happening is, all connected user getting this socket stream and getting their model updated too, but as page refreshed it is always only what that query pulls, only user specific data.
this is how I get user specific data
$rootScope.getUserGoals = function (userid){
//get current todo task views
$http.get('/api/goals/name/'+userid).success(function(goals) {
$rootScope.userGoalArr = goals;
socket.syncUpdates('goal', $rootScope.userGoalArr);
});
};
then it displays in view with this
<ul class = "holdTaskul" ng-repeat="view in filtered = userGoalArr | orderBy:'-created'">
I looked up on google and search a lot to figure out how to separate each user socket stream and attached them to their login credential. I have come across this socket-jwt and session token. I am not expert on socket. I would much appreciate if someone could point me to right direction about this, what I am intending to do..
my socket configuration is as follows for socket.syncUpdate function
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem);
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
I have also configured socket for sending auth token by this
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
query: 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
and used socketio-jwt to send the secret session to socket service by that
module.exports = function (socketio) {
socketio.use(require('socketio-jwt').authorize({
secret: config.secrets.session,
handshake: true
}));

Connection state with doowb/angular-pusher

I am trying to build an Angular project with Pusher using the angular-pusher wrapper. It's working well but I need to detect when the user loses internet briefly so that they can retrieve missed changes to data from my server.
It looks like the way to handle this is to reload the data on Pusher.connection.state('connected'...) but this does not seem to work with angular-pusher - I am receiving "Pusher.connection" is undefined.
Here is my code:
angular.module('respondersapp', ['doowb.angular-pusher']).
config(['PusherServiceProvider',
function(PusherServiceProvider) {
PusherServiceProvider
.setToken('Foooooooo')
.setOptions({});
}
]);
var ResponderController = function($scope, $http, Pusher) {
$scope.responders = [];
Pusher.subscribe('responders', 'status', function (item) {
// an item was updated. find it in our list and update it.
var found = false;
for (var i = 0; i < $scope.responders.length; i++) {
if ($scope.responders[i].id === item.id) {
found = true;
$scope.responders[i] = item;
break;
}
}
if (!found) {
$scope.responders.push(item);
}
});
Pusher.subscribe('responders', 'unavail', function(item) {
$scope.responders.splice($scope.responders.indexOf(item), 1);
});
var retrieveResponders = function () {
// get a list of responders from the api located at '/api/responders'
console.log('getting responders');
$http.get('/app/dashboard/avail-responders')
.success(function (responders) {
$scope.responders = responders;
});
};
$scope.updateItem = function (item) {
console.log('updating item');
$http.post('/api/responders', item);
};
// load the responders
retrieveResponders();
};
Under this setup how would I go about monitoring connection state? I'm basically trying to replicate the Firebase "catch up" functionality for spotty connections, Firebase was not working overall for me, too confusing trying to manage multiple data sets (not looking to replace back-end at all).
Thanks!
It looks like the Pusher dependency only exposes subscribe and unsubscribe. See:
https://github.com/doowb/angular-pusher/blob/gh-pages/angular-pusher.js#L86
However, if you access the PusherService you get access to the Pusher instance (the one provided by the Pusher JS library) using PusherService.then. See:
https://github.com/doowb/angular-pusher/blob/gh-pages/angular-pusher.js#L91
I'm not sure why the PusherService provides a level of abstraction and why it doesn't just return the pusher instance. It's probably so that it can add some of the Angular specific functionality ($rootScope.$broadcast and $rootScope.$digest).
Maybe you can set the PusherService as a dependency and access the pusher instance using the following?
PusherService.then(function (pusher) {
var state = pusher.connection.state;
});
To clarify #leggetters answer, you might do something like:
app.controller("MyController", function(PusherService) {
PusherService.then(function(pusher) {
pusher.connection.bind("state_change", function(states) {
console.log("Pusher's state changed from %o to %o", states.previous, states.current);
});
});
});
Also note that pusher-js (which angular-pusher uses) has activityTimeout and pongTimeout configuration to tweak the connection state detection.
From my limited experiments, connection states can't be relied on. With the default values, you can go offline for many seconds and then back online without them being any the wiser.
Even if you lower the configuration values, someone could probably drop offline for just a millisecond and miss a message if they're unlucky.

In GAE Channel API the onmessage is not called

I am building an app for GAE using python API. It is running here. It is a multi-player game. I use the Channel API to communicate game state between players.
But in the app engine the onmessage handler of the channel is not called. The onopen handler is called. onerror or onclose are not called as well. Weird thing is this works perfectly in the local development server.
Is it possible that something like this can work on the development server but not in the app engine itself?
I'll be really really glad if someone can look into following description of my app and help me to figure out what has happened. Thank you.
I looked into this and this questions, but I haven't done those mistakes.
<script>
sendMessage = function(path, opt_param, opt_param2) {
path += '?g=' + state.game_key;
if (opt_param) {
path += '&' + opt_param;
}
if (opt_param2) {
path += '&' + opt_param2;
}
var xhr = new XMLHttpRequest();
xhr.open('POST', path, true);
xhr.send();
};
Above function is used to make a post request to the server.
onOpened = function() {
sendMessage('/resp');
console.log('channel opened');
};
Above is the function I want to be called when the channel is open for the first time. I send a post to the '/resp' address.
onMessage = function(m) {
console.log('message received');
message = JSON.parse(m.data);
//do stuff with message here
};
I want to process the response I get from that request in the above function.
following are onerror and onclose handlers.
onError = function() {
console.log('error occured');
channel = new goog.appengine.Channel('{{ token }}');
socket = channel.open();
};
onClose = function() {
console.log('channel closed');
};
channel = new goog.appengine.Channel('{{ token }}');
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onclose = onClose;
socket.onerror = onError;
</script>
This script is at the top of body tag. This works fine in my local development server. But on the app engine,
onOpen function is called.
I can see the request to /resp in the sever logs.
but onMessage is never called. The log 'message received' is not present in the console.
this is the server side.
token = channel.create_channel(user.user_id() + game.user1.user_id() )
url = users.create_logout_url(self.request.uri)
template_values = {
'token' : token,
'id' : pid,
'game_key' : str(game.user1.user_id()),
'url': url
}
path = os.path.join(os.path.dirname(__file__), 'game.html')
self.response.out.write(template.render(path, template_values))
and this is in the request handler for '/resp' request. My application is a multi-player card game. And I want to inform other players that a new player is connected. Even the newly connected player will also get this message.
class Responder(webapp2.RequestHandler):
def post(self):
user = users.get_current_user()
game = OmiGame.get_by_key_name(self.request.get('g'))
if game.user1:
channel.send_message(game.user1.user_id() + game.user1.user_id() , create_message('%s joined.' % user.nickname()))
if game.user2:
channel.send_message(game.user2.user_id() + game.user1.user_id() , create_message('%s joined.' % user.nickname()))
EDIT : user1 is the user who created the game. I want tokens of other players' to be created by adding the user1's user_id and the relevant users user_id. Could something go wrong here?
So when I try this on the local dev server I get these messages perfectly fine. But on the GAE onMessage is not called. This is my app. When the create button is clicked page with above script is loaded and "playernickname connected" should be displayed.
The channel behavior on the dev server and production are somewhat different. On the dev server, the channel client just polls http requests frequently. On production, comet style long polling is used.
I suspect there may be a problem with making the XHR call inside the onOpened handler. In Chrome at least, I see that the next talkgadget GET request used by the channel API is cancelled.
Try calling sendMessage('/resp') outside of the onMessage function. Perhaps enqueue it to get run by using setTimeout so it's called later after you return.

Resources