I created a chat app using socket.io, everything works very well except for when one of the users refresh. For instance:
If user A joins room 1, and user B joins room 1, they can communicate smoothly. If user A refreshes his browser, he can still send messages to user B in real time, but user B no longer sends user A messages in real time.
For some reason, when a user refreshes the page, he is no longer able to receive messages in real time. Here is how I have setup my chat system:
server side:
server.listen(port);
var io = require('socket.io').listen(server);
io.on('connection', function(socket){
console.log("User Connected");
socket.on('comment', function(data){
//socket.broadcast.emit('comment', data);
console.log('server sidecomment');
socket.broadcast.to(data.chatId).emit('comment',data);
});
socket.on('disconnect', function(){
console.log('user disconnected');
});
socket.on('join:room', function(data){
socket.join(data.chatId);
socket.broadcast.to(data.chatId).emit('join:room', data)
console.log('joined room: ' + data.chatId);
});
socket.on('leave:room', function(data){
socket.leave(data.chatId);
console.log('left room: ' + data.chatId);
})
socket.on('typing', function(data){
console.log('server side start typing');
console.log(data);
socket.broadcast.to(data.chatId).emit('typing',data);
});
socket.on('stoptyping', function(data){
console.log('server side stop typing');
socket.broadcast.to(data.chatId).emit('stoptyping');
});
});
client side:
$scope.$on("$destroy", function() {
console.log('leaving page');
socket.emit('leave:room', {
'chatId': post.chatId
});
var leaveMessage = auth.currentUser() + ' has left';
posts.addComment(post._id, {
body: leaveMessage,
author: 'server'
}).success(function(comment) {
$scope.post.comments.push(comment);
});
});
socket.on('comment', function(data) {
console.log('client side comment');
$scope.post.comments.push(data);
});
socket.on('join:room', function(data) {
var joinedMessage = {
body: data.author + ' has joined',
author: 'server'
};
$scope.post.comments.push(joinedMessage);
});
$scope.whenTyping = function() {
socket.emit('typing', {
'chatId': post.chatId,
'author': auth.currentUser()
});
}
$scope.notTyping = function() {
socket.emit('stoptyping', {
'chatId': post.chatId
});
}
socket.on('typing', function(data) {
$scope.typing = data.author + ' is typing';
});
socket.on('stoptyping', function() {
$scope.typing = '';
});
My question is: Is the socket being timed out? If it is, is there a way I can prevent it from being timed out?
Related
I have problem with socket.io. In my code router.post(/comment,...) saving user comments in database (using mongoose) and I am trying emit this save. In controller function readMoreCourse is to get and display all comments from database (and question how use socket to this function that using ng-reapat display comment in real-time). Function AddComment is on client side chceck valid form and next post comment to database.
My question: How in real-time save and display user comment using angular (ng-repeat?) and socket.io? Honestly I making this first time, and I have short time, thanks for any help.
Server
io.on('connection', function(socket){
socket.emit('comment', function(){
console.log('Comment emitted')
})
socket.on('disconnect', function(){
})
})
API
router.post('/comment', function(req, res) {
Product.findOne({ _id: req.body._id }, function(err, product){
if(err) {
res.json({ success:false, message: 'Course not found' })
} else {
User.findOne({ username: req.decoded.username }, function(err, user){
if(err){
res.json({ success:false, message: 'Error'})
} else {
product.comments.push({
body: req.body.comment,
author: user.username,
date: new Date(),
});
product.save(function(err){
if(err) throw err
res.json({ success: true, message: 'Comment added })
**io.emit('comment', msg);**
})
}
})
}
})
})
controller
Socket.connect();
User.readMoreCourse($routeParams.id).then(function(data){
if(data.data.success){
app.comments = data.data.product.comments;
} else {
$window.location.assign('/404');
}
});
app.AddComment = function(comment, valid) {
if(valid){
var userComment = {};
userComment.comment = app.comment;
Socket.on('comment', User.postComment(userComment).then(function(data){
if(data.data.success){
$timeout(function(){
$scope.seeMore.comment = '';
},2000)
} else {
app.errorMsg = data.data.message;
}
}));
} else {
app.errorMsg = 'Error';
}
}
$scope.$on('$locationChangeStart', function(event){
Socket.disconnect(true);
})
factory
userFactory.readMoreCourse = function(id) {
return $http.get('/api/seeMore/' + id)
}
userFactory.postComment = function(comment){
return $http.post('/api/comment', comment);
}
.factory('Socket', function(socketFactory){
return socketFactory()
})
In your socket factory, initialize socket.io emit and on events.
app.factory('socket', ['$rootScope', function($rootScope) {
var socket = io.connect();
return {
on: function(eventName, callback){
socket.on(eventName, callback);
},
emit: function(eventName, data) {
socket.emit(eventName, data);
}
};
}]);
and call this from controller
app.controller('yourController', function($scope, socket) {
User.postComment(userComment).then(function(data){
if(data.data.success){
$timeout(function(){
$scope.seeMore.comment = '';
},2000);
// Emit new comment to socket.io server
socket.emit("new comment", userComment);
} else {
app.errorMsg = data.data.message;
}
});
// other clients will listen to new events here
socket.on('newComment', function(data) {
console.log(data);
// push the data.comments to your $scope.comments
});
from socket.io server
io.on('connection', function(socket) {
// listen for new comments from controller and emit it to other clients
socket.on('new comment', function(data) {
io.emit('newComment', {
comment: data
});
});
});
EDIT:
If you just want to push from server side,
io.on('connection', function(socket) {
// after saving your comment to database emit it to all clients
io.emit('newComment', {
comment: data
});
});
and remove this emit code from controller:
socket.emit("new comment", userComment);
But this method can be tricky because the user who posts the comment should immediately see the comment added to the post. If you let socket.io to handle this there will be a few seconds lag for the guy who posted the comment.
I am implementing push notification my push notification with ionic platform (ionic io) working properly now I have to take out that device token and send it to my server.
below is my ap.js code:
var push = new Ionic.Push({
"debug": true
});
push.register(function(token) {
console.log("My Device token:",token.token);
push.saveToken(token); // persist the token in the Ionic Platform
});
This is my login for
$scope.login = function () {
$http({
method: "post",
url: "http://200.189.253.200:8081/employee-connect/oauth/token",
data: "username="+$scope.username+"&password="+$scope.password+"&grant_type=password&scope=read write&client_secret=my-secret-token-to-change-in-production&client_id=employeeConnectapp2",
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.success(function (data){
window.localStorage.setItem("token_type", data.token_type);
window.localStorage.setItem("token", data.access_token);
$state.go('tabsController.home');
})
.error(function(data) {
var alertPopup = $ionicPopup.alert({
title: 'Login failed!',
template: 'Please check your credentials!'
});
});
}
In the success call I have to send token by fetching it from console but don't know how to do. Kindly help me.
Install this plugin
cordova plugin add https://github.com/phonegap-build/PushPlugin.git
and inside your .run funtion do this
var androidConfig = {
"senderID": "xxxxxxxx", //you should place your gcm project number
};
document.addEventListener("deviceready", function(){
$cordovaPush.register(androidConfig).then(function(result) {
// Success
}, function(err) {
// Error
})
$rootScope.$on('$cordovaPush:notificationReceived', function(event, notification) {
switch(notification.event) {
case 'registered':
if (notification.regid.length > 0 ) {
alert('registration ID = ' + notification.regid);
//here you will see the device token in alert.
MyService.setDeviceID(notification.regid);
//here i have used MyService to access the regiser id inside my controller
}
break;
case 'message':
// this is the actual push notification. its format depends on the data model from the push server
alert('message = ' + notification.message + ' msgCount = ' + notification.msgcnt);
break;
case 'error':
alert('GCM error = ' + notification.msg);
break;
default:
alert('An unknown GCM event has occurred');
break;
}
});
}, false);
For more information of getting device id look this
Look this answer that i have posted to get the Deal with GCM push notification Ionic Push Notifications: getPushPlugin is undefined
I'm very new to angular, so my knowledge is based on tutorials and even then I don't succeed.
I need to authenticate using a google account. That works, I get a token where my api calls could be authorized with. But after login the pop up window should dismiss and I should be redirected to the homepage. This doesn't work.
this is my controller
angular.module('MyApp').controller('loginController', ['$scope', '$auth', '$location','loginService', loginController]);
function loginController($scope, $auth, $location, loginService) {
$scope.authenticate = function(provider) {
$auth.authenticate(provider).then(function(data) {
loginService.saveToken(data.data.token);
console.log('You have successfully signed in with ' + provider + '!');
$location.path('http://localhost/#/home');
});
};
};
in app.js I have my configuration. this is not my work but a friend who is an intern as wel as me, he is responsible for a mobile application, where he uses the same function to get his token, and it works.
authProvider.google({
clientId: CLIENT_ID,
redirectUri: 'http://localhost:3000/api/users/signIn'
});
$authProvider.storage = 'localStorage'; // or 'sessionStorage'
$authProvider.loginRedirect = 'http://localhost/#/home';
This is the controller in node where the url is redirected to (google developer console)
router.get('/signIn', function(req, res) {
//console.log(req);
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
if (!err) {
https.get("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + tokens.access_token, function(response) {
// Continuously update stream with data
var body = '';
response.setEncoding('utf8');
response.on('data', function(d) {
body += d;
});
// Data fetched
response.on('end', function() {
var parsed = JSON.parse(body);
// Check if client_id is from the right app
if (parsed.issued_to == '343234242055-vd082vo0o8r8lmfvp1a973736fd98dht.apps.googleusercontent.com') {
User.getGoogleId(parsed.user_id, function(err, user) {
if (err) {
res.status(500).send({
message: 'not authorized app'
});
}
// No user returned, create one
if (!user) {
// Request user info
oauth2Client.setCredentials(tokens);
plus.people.get({
userId: 'me',
auth: oauth2Client
}, function(err, plusUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
// Create new user
User.create(plusUser.name.givenName, plusUser.name.familyName, (plusUser.name.givenName + "." + plusUser.name.familyName + "#cozmos.be").toLowerCase(), parsed.user_id, function(err, newUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
});
} else {
// Return user
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
// if not right app, return unauthorized response
else {
res.status(500).send({
message: 'not authorized app'
});
}
});
});
}
});
});
So I login, I get asked to give permission to the application to use my account info, I get a json response where I can see my name, email and token, and that's it
Even within the company where I work, no one could find an answer. So I came with a solution myself. I don't use satellizer anymore.
.when('/access_token=:access_token', {
template: '',
controller: function($window, $http, $location, $rootScope) {
var hash = $location.path().substr(1);
var splitted = hash.split('&');
var params = {};
for (var i = 0; i < splitted.length; i++) {
var param = splitted[i].split('=');
var key = param[0];
var value = param[1];
params[key] = value;
$rootScope.accesstoken = params;
}
console.log(params.access_token);
var json = {
Token: params.access_token
};
$window.localStorage['token'] = params.access_token;
$http.post('http://localhost:3000/api/users/signIn', json).success(function(data, status) {
console.log(data);
}).error(function(err) {
console.log(err);
});
$location.path("/home");
}
/*controller: 'createNewsFeed',
templateUrl: 'homepage.html'*/
}).
So redirect the page by itself. Because the authentication works on the backend side, I can get a access token, which is the only thing I really need for future use of my rest api. I defined a route where, after receiving the json with the token, my browser is manually redirected to with $window.location. So when that page is loaded (not visible for the user, it goes too fast to notice) I analyse the token, save the token, analyse authentication, when that is successful I manually redirect to the homepage.
I am using ionic framework for my android app and MEANJS on my server. I am using Web Sockets to get realtime data. While the server side web application updates automatically every time a CRUD happens in the android application, the android app does not update automatically when a change is made on the server side.
Android App Service(AngularJS)
.service('Socket', ['Authentication', '$state', '$timeout',
function (Authentication, $state, $timeout) {
// Connect to Socket.io server
this.connect = function () {
// Connect only when authenticated
if (Authentication.user) {
this.socket = io('https://cryptic-savannah-60962.herokuapp.com');
}
};
this.connect();
// Wrap the Socket.io 'on' method
this.on = function (eventName, callback) {
if (this.socket) {
this.socket.on(eventName, function (data) {
$timeout(function () {
callback(data);
});
});
}
};
// Wrap the Socket.io 'emit' method
this.emit = function (eventName, data) {
if (this.socket) {
this.socket.emit(eventName, data);
}
};
// Wrap the Socket.io 'removeListener' method
this.removeListener = function (eventName) {
if (this.socket) {
this.socket.removeListener(eventName);
}
};
}
Client Side Controller
if (!Socket.socket && Authentication.user) {
Socket.connect();
}
Socket.on('orderCreateError', function (response) {
$scope.error = response.message;
});
Socket.on('orderCreateSuccess', function (response) {
if ($scope.orders) {
$scope.orders.unshift(response.data);
}
});
Socket.on('orderUpdateSuccess', function (response) {
if ($scope.orders) {
// not the most elegant way to reload the data, but hey :)
$scope.orders = Orders.query();
}
});
Server Controller(NodeJS)
socket.on('orderUpdate', function (data) {
var user = socket.request.user;
// Find the Order to update
Order.findById(data._id).populate('user', 'displayName').exec(function (err, order) {
if (err) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else if (!order) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: 'No order with that identifier has been found' });
} else {
order.name = data.name;
order.phone = data.phone;
order.water = data.water;
order.waiter = data.waiter;
order.napkin = data.napkin;
order.complete = data.complete;
order.rating = data.rating;
order.payment_mode = data.payment_mode;
order.order_source = data.order_source;
order.orderfood = data.orderfood;
order.save(function (err) {
if (err) {
// Emit an error response event
io.sockets.emit('orderUpdateError', { data: data, message: errorHandler.getErrorMessage(err) });
} else {
// Emit a success response event
io.sockets.emit('orderUpdateSuccess', { data: order, updatedBy: user.displayName, updatedAt: new Date(Date.now()).toLocaleString(), message: 'Updated' });
}
});
}
});
});
You have two emit channels on your server side but neither event is handled on the client side.
Per socket.io docs, you need something like:
socket.on('orderUpdateSuccess', function (data) {
// do something inside the app that will update the view
console.log(data);
Orders.update(data); // assuming you have a service called Orders to keep track of live data -- don't forget [$scope.$apply][2]
});
Using your example code
Socket.on('orderUpdateSuccess', function (response) {
if ($scope.orders) {
$scope.$apply(function() {
// not the most elegant way to reload the data, but hey :)
$scope.orders = Orders.query();
});
}
});
Here i need to search name in scroll,for that i send search data query string in get call but i need to that in post.
Here is my server and client controller route and service.Also here i handling search from server side.How to post data which user has been searched ,and pass that to client and server side.
client controller service:
'use strict';
angular.module('details').factory('DetailService', ['$resource',
function($resource) {
return $resource('details', {
},
searchUsers:{
method: 'GET',
}
});
}
]);
Angular controller:
$scope.searchServer = function(searchData){
DetailService.searchUsers({search:searchData},function(response){
}, function(error){
$scope.status = 'Unable to load customer data: ' + error.message;
});
}
my Server side controller:
exports.searchCust = function (req, res) {
var strWhere = {
corporateName: search
};
db.Customer.findAll({
where: [strWhere],
}).then(function (customers) {
if (!customers) {
return res.status(400).send({
message: 'Customer not found.'
});
} else {
res.jsonp(customers);
}
})
};
my server sideroute:
app.route('/details').all(customersPolicy.isAllowed)
.get(details.searchCust);
app.param('search', details.searchCust);
};
I didn't try it out in all details as it looks like it was copy and pasted together without reading the basics. However, if you want POST requests, you need to set them both in the node-code and the Angular code, see below. What's more, Angular doesn't use JSONP, it uses JSON, so you need to set that. In the searchUsers-resource-call you only implemented the error-branch, so the results would just vanish. You'll find them in $scope.searchResults now.
client controller service:
'use strict';
angular.module('details').factory('DetailService', ['$resource',
function($resource) {
return $resource('details', {},
searchUsers: {
method: 'POST',
}
});
}]);
Angular controller:
$scope.searchServer = function(searchData) {
DetailService.searchUsers({
search: searchData
}, function(response) {
$scope.status = "OK";
$scope.searchResults = response;
}, function(error) {
$scope.status = 'Unable to load customer data: ' + error.message;
});
}
my Server side controller
exports.searchCust = function(req, res) {
var strWhere = {
corporateName: search
};
db.Customer.findAll({
where: [strWhere],
}).then(function(customers) {
if (!customers) {
return res.status(400).send({
message: 'Customer not found.'
});
} else {
res.json(customers);
}
})
};
my server sideroute:
app.route('/details').all(customersPolicy.isAllowed)
.post(details.searchCust);
app.param('search', details.searchCust);
};